Haskell 性能:组合与应用程序?

2024-03-13

我看到了一些关于函数组合和应用程序之间的相似点和差异以及实现它的各种方法的问题,但是有一件事开始让我有点困惑(据我搜索,还没有被问到)是关于函数组合和应用程序之间的差异表现。

当我学习 F# 时,我爱上了管道操作符|>,这在 haskell 的反向应用中具有等价物&。但在我看来,F# 变体无疑更漂亮(而且我不认为我是唯一的)。

现在,我们可以轻松地将管道操作符破解到 haskell 中:

(|>) x f = f x

它就像一个魅力!问题解决了!

管道(F# 和我们的 haskell 技巧中的管道)之间的最大区别在于它不组合函数,而是基于函数应用程序。它采用左侧的值并将其传递给右侧的函数,这与组合相反,组合采用 2 个函数并返回另一个函数,然后该函数可以用作任何常规函数。

至少对我来说,这使得代码更加漂亮,因为您只使用一个运算符来引导整个函数从参数到最终值的信息流,因为使用基本组合(或 >>>),您无法在左侧使其穿过“链条”。

但从性能的角度来看,看看这些常规选项,结果应该完全相同:

f x = x |> func1 |> func2 |> someLambda |> someMap |> someFold |> show

f x = x & (func1 >>> func2 >>> someLambda >>> someMap >>> someFold >>> show)

f x = (func1 >>> func2 >>> someLambda >>> someMap >>> someFold >>> show) x

哪一种最快,是基于重复应用的一种,还是基于组合和单次应用的一种?


应该没有什么区别,只要(|>) and (>>>)内联。让我们编写一个使用四个不同函数的示例,其中两个采用 F# 风格,两个采用 Haskell 风格:

import Data.Char (isUpper)

{-# INLINE (|>) #-}
(|>) :: a -> (a -> b) -> b
(|>) x f = f x

{-# INLINE (>>>) #-}
(>>>) :: (a -> b) -> (b -> c) -> a -> c
(>>>) f g x = g (f x)

compositionF :: String -> String
compositionF = filter isUpper >>> length >>> show 

applicationF :: String -> String
applicationF x = x |> filter isUpper |> length |> show 

compositionH :: String -> String
compositionH = show . length . filter isUpper

applicationH :: String -> String
applicationH x = show $ length $ filter isUpper $ x

main :: IO ()
main = do
  getLine >>= putStrLn . compositionF  -- using the functions
  getLine >>= putStrLn . applicationF  -- to make sure that
  getLine >>= putStrLn . compositionH  -- we actually get the
  getLine >>= putStrLn . applicationH  -- corresponding GHC core

如果我们编译我们的代码-ddump-simpl -dsuppress-all -O0 we get:

==================== Tidy Core ====================
Result size of Tidy Core = {terms: 82, types: 104, coercions: 0}

-- RHS size: {terms: 9, types: 11, coercions: 0}
>>>_rqe
>>>_rqe =
  \ @ a_a1cE @ b_a1cF @ c_a1cG f_aqr g_aqs x_aqt ->
    g_aqs (f_aqr x_aqt)

-- RHS size: {terms: 2, types: 0, coercions: 0}
$trModule1_r1gR
$trModule1_r1gR = TrNameS "main"#

-- RHS size: {terms: 2, types: 0, coercions: 0}
$trModule2_r1h6
$trModule2_r1h6 = TrNameS "Main"#

-- RHS size: {terms: 3, types: 0, coercions: 0}
$trModule
$trModule = Module $trModule1_r1gR $trModule2_r1h6

-- RHS size: {terms: 58, types: 73, coercions: 0}
main
main =
  >>
    $fMonadIO
    (>>=
       $fMonadIO
       getLine
       (. putStrLn
          (>>>_rqe
             (>>>_rqe (filter isUpper) (length $fFoldable[]))
             (show $fShowInt))))
    (>>
       $fMonadIO
       (>>=
          $fMonadIO
          getLine
          (. putStrLn
             (\ x_a10M ->
                show $fShowInt (length $fFoldable[] (filter isUpper x_a10M)))))
       (>>
          $fMonadIO
          (>>=
             $fMonadIO
             getLine
             (. putStrLn
                (. (show $fShowInt) (. (length $fFoldable[]) (filter isUpper)))))
          (>>=
             $fMonadIO
             getLine
             (. putStrLn
                (\ x_a10N ->
                   show $fShowInt (length $fFoldable[] (filter isUpper x_a10N)))))))

-- RHS size: {terms: 2, types: 1, coercions: 0}
main
main = runMainIO main

So >>>如果我们不启用优化,则不会内联。但是,如果我们启用优化,您将看不到>>> or (.)根本不。我们的功能略有不同,因为(.)在那个阶段没有被内联,但这在某种程度上是预料之中的。

如果我们添加{-# NOINLINE … #-}对于我们的函数并启用优化,我们发现这四个函数根本没有区别:

$ ghc -ddump-simpl -dsuppress-all -O2 Example.hs
[1 of 1] Compiling Main             ( Example.hs, Example.o )

==================== Tidy Core ====================
Result size of Tidy Core = {terms: 261, types: 255, coercions: 29}

-- RHS size: {terms: 2, types: 0, coercions: 0}
$trModule2
$trModule2 = TrNameS "main"#

-- RHS size: {terms: 2, types: 0, coercions: 0}
$trModule1
$trModule1 = TrNameS "Main"#

-- RHS size: {terms: 3, types: 0, coercions: 0}
$trModule
$trModule = Module $trModule2 $trModule1

Rec {
-- RHS size: {terms: 29, types: 20, coercions: 0}
$sgo_r574
$sgo_r574 =
  \ sc_s55y sc1_s55x ->
    case sc1_s55x of _ {
      [] -> I# sc_s55y;
      : y_a2j9 ys_a2ja ->
        case y_a2j9 of _ { C# c#_a2hF ->
        case {__pkg_ccall base-4.9.1.0 u_iswupper Int#
                                     -> State# RealWorld -> (# State# RealWorld, Int# #)}_a2hE
               (ord# c#_a2hF) realWorld#
        of _ { (# ds_a2hJ, ds1_a2hK #) ->
        case ds1_a2hK of _ {
          __DEFAULT -> $sgo_r574 (+# sc_s55y 1#) ys_a2ja;
          0# -> $sgo_r574 sc_s55y ys_a2ja
        }
        }
        }
    }
end Rec }

-- RHS size: {terms: 15, types: 14, coercions: 0}
applicationH
applicationH =
  \ x_a12X ->
    case $sgo_r574 0# x_a12X of _ { I# ww3_a2iO ->
    case $wshowSignedInt 0# ww3_a2iO []
    of _ { (# ww5_a2iS, ww6_a2iT #) ->
    : ww5_a2iS ww6_a2iT
    }
    }

Rec {
-- RHS size: {terms: 29, types: 20, coercions: 0}
$sgo1_r575
$sgo1_r575 =
  \ sc_s55r sc1_s55q ->
    case sc1_s55q of _ {
      [] -> I# sc_s55r;
      : y_a2j9 ys_a2ja ->
        case y_a2j9 of _ { C# c#_a2hF ->
        case {__pkg_ccall base-4.9.1.0 u_iswupper Int#
                                     -> State# RealWorld -> (# State# RealWorld, Int# #)}_a2hE
               (ord# c#_a2hF) realWorld#
        of _ { (# ds_a2hJ, ds1_a2hK #) ->
        case ds1_a2hK of _ {
          __DEFAULT -> $sgo1_r575 (+# sc_s55r 1#) ys_a2ja;
          0# -> $sgo1_r575 sc_s55r ys_a2ja
        }
        }
        }
    }
end Rec }

-- RHS size: {terms: 15, types: 15, coercions: 0}
compositionH
compositionH =
  \ x_a1jF ->
    case $sgo1_r575 0# x_a1jF of _ { I# ww3_a2iO ->
    case $wshowSignedInt 0# ww3_a2iO []
    of _ { (# ww5_a2iS, ww6_a2iT #) ->
    : ww5_a2iS ww6_a2iT
    }
    }

Rec {
-- RHS size: {terms: 29, types: 20, coercions: 0}
$sgo2_r576
$sgo2_r576 =
  \ sc_s55k sc1_s55j ->
    case sc1_s55j of _ {
      [] -> I# sc_s55k;
      : y_a2j9 ys_a2ja ->
        case y_a2j9 of _ { C# c#_a2hF ->
        case {__pkg_ccall base-4.9.1.0 u_iswupper Int#
                                     -> State# RealWorld -> (# State# RealWorld, Int# #)}_a2hE
               (ord# c#_a2hF) realWorld#
        of _ { (# ds_a2hJ, ds1_a2hK #) ->
        case ds1_a2hK of _ {
          __DEFAULT -> $sgo2_r576 (+# sc_s55k 1#) ys_a2ja;
          0# -> $sgo2_r576 sc_s55k ys_a2ja
        }
        }
        }
    }
end Rec }

-- RHS size: {terms: 15, types: 15, coercions: 0}
compositionF
compositionF =
  \ x_a1jF ->
    case $sgo2_r576 0# x_a1jF of _ { I# ww3_a2iO ->
    case $wshowSignedInt 0# ww3_a2iO []
    of _ { (# ww5_a2iS, ww6_a2iT #) ->
    : ww5_a2iS ww6_a2iT
    }
    }

Rec {
-- RHS size: {terms: 29, types: 20, coercions: 0}
$sgo3_r577
$sgo3_r577 =
  \ sc_s55d sc1_s55c ->
    case sc1_s55c of _ {
      [] -> I# sc_s55d;
      : y_a2j9 ys_a2ja ->
        case y_a2j9 of _ { C# c#_a2hF ->
        case {__pkg_ccall base-4.9.1.0 u_iswupper Int#
                                     -> State# RealWorld -> (# State# RealWorld, Int# #)}_a2hE
               (ord# c#_a2hF) realWorld#
        of _ { (# ds_a2hJ, ds1_a2hK #) ->
        case ds1_a2hK of _ {
          __DEFAULT -> $sgo3_r577 (+# sc_s55d 1#) ys_a2ja;
          0# -> $sgo3_r577 sc_s55d ys_a2ja
        }
        }
        }
    }
end Rec }

-- RHS size: {terms: 15, types: 14, coercions: 0}
applicationF
applicationF =
  \ x_a12W ->
    case $sgo3_r577 0# x_a12W of _ { I# ww3_a2iO ->
    case $wshowSignedInt 0# ww3_a2iO []
    of _ { (# ww5_a2iS, ww6_a2iT #) ->
    : ww5_a2iS ww6_a2iT
    }
    }
...

All go函数完全相同(没有变量名),并且application*是相同的composition*。因此,继续在 Haskell 中创建您自己的 F# prelude,应该不会有任何性能问题。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Haskell 性能:组合与应用程序? 的相关文章

随机推荐

  • Chrome 扩展程序身份:OAuth2 请求失败:服务响应错误:“错误请求”

    我跟着https developer chrome com extensions tut oauth https developer chrome com extensions tut oauth完全一步一步 但我被困在 https dev
  • 如何使用 ASP.Net MVC 制作向导

    我们的网站有多个 向导 在多个页面上收集各种数据 并且直到最后一步才能将其提交到数据库 使用 ASP Net MVC 制作这样的向导的最佳 正确方法是什么 编辑 我的老板现在说 没有 javascript 关于如何绕过该限制有什么想法吗 我
  • 在 D 中制作结构体的堆副本

    如何创建堆栈上结构的垃圾收集副本 来自 C 背景 我的第一个猜测是像下面这样的复制构造函数 但它对于 D 来说似乎不太惯用 而且我在我看过的任何 D 项目中都没有看到过这样的复制构造函数 struct Foo immutable int b
  • 如何从控制器类打开 JavaFX FileChooser?

    我的问题是所有使用的例子FileChooser要求你通过一个阶段 唯一的问题是我的 UI 是在fxml文件 它使用与主阶段分开的控制器类 FXML protected void locateFile ActionEvent event Fi
  • 在 R 中,增长列表与增长向量一样低效吗?

    some list lt list for i in 1 1000 some list i lt i 我想知道如果 some list 是一个向量 这是否同样低效 答案似乎是 是 您可以对其进行基准测试 f function some li
  • 列表框.SelectedItem

    我有一个ListBox与填充的物品TextBoxes 从列表中进行选择时如何识别所选的文本字符串ListBox 这是我的 XAML 代码ListBox
  • CSS 内嵌边框

    我需要创建一个纯色插入边框 这是我正在使用的 CSS 代码 border 10px inset rgba 51 153 0 0 65 不幸的是 这会创建 3D 脊状边框 忽略正方形和黑色描述框 你可以使用box shadow 可能 some
  • 如何从 SvelteKit 应用程序发送安全的 API 请求,而不在客户端显示 API 密钥?

    我正在将 Supabase 用于新的 Sveltekit 应用程序 这个模板 https github com joshnuss svelte supabase 目前 我正在客户端传递 Supabase 密钥 如下所示 const supa
  • 相当于 C++ 中 Rust 的特定模板用法

    Rust 中是否有一个功能可以使这样的事情成为可能 据我所知 Rust 的泛型函 数不可能做到这一点 因为它们仅适用于数据类型 而不适用于值 include
  • 在asp.net core 3.1中使用log4net和Microknights登录数据库不起作用

    我看过很多文章说 net core 不支持 AdoNetAppender 但我们可以使用 MicroKnights 来做同样的事情 我正在尝试使用相同的方法在 net core 3 1 应用程序中实现数据库日志记录 但仍然没有成功 我放置在
  • 网页抓取中如何获取重定向的URL?

    我想要的只是请求实际 url 后重定向的 url 这是实际的网址https metric picodi net us r 19761 当我使用此 url 在浏览器上按 Enter 键时 它会将我重定向到这样的 url https www o
  • 仅将天数和时间(小时 x 分钟 x 秒)转换为时间

    我有一个数据框 在其中比较两个不同日期之间的差异以获得小时和分钟的差异 例如 start date 2018 07 03 16 03 00 data final 2018 07 05 00 00 00 duration data final
  • 交叉联接行为 (SQLServer 2008)

    我一直在尝试找出我的查询的问题 该查询实际上是由 hibernate 从 HQL 生成的 但生成的 SQL 并没有达到我的预期 稍微修改 SQL 会产生正确的结果 但我不确定为什么修改会产生任何差异 原始查询 不返回任何行 select s
  • 如何在 Rust 中惯用地替换特定字符?

    所以我有字符串 Hello World 并想替换 和 这样新字符串就是 Hello World 在 Ruby 中 我们可以使用以下命令轻松完成此操作gsub method Hello World gsub 如何在 Rust 中惯用地做到这一
  • 创建随机二进制文件

    我正在尝试使用 python 创建一个随机二进制文件 这就是我已经得到的 f open filename wb for i in xrange size kb for ii in xrange 1024 4 f write struct p
  • MarshalAs 嵌套结构

    我有两个 C 结构 在从 C 调用 DLL 方法时必须将它们作为参数发送 例如 让我们将它们定义为 struct A int data struct B int MoreData A SomeData 我需要从 C 调用的方法具有以下签名
  • 在 IE 中打开新选项卡

    我正在使用以下代码在 Internet Explorer 中打开网站 ProcessStartInfo startInfo new ProcessStartInfo Arguments http www example com FileNa
  • 更改 use 元素上的 defs 中定义的属性

    如何通过脚本来更改 defs 中定义的 use element 的样式 我试图进入 w3c 工作草案界面 但我迷失在那个迷宫中
  • 在自动布局中将子视图的 X 居中会引发“未准备好约束”

    我有一个自定义 UIView 子类 它是通过笔尖初始化的 In awakeFromNib 我正在创建一个子视图并尝试将其置于其超级视图的中心 self setInteralView UIView alloc init self intern
  • Haskell 性能:组合与应用程序?

    我看到了一些关于函数组合和应用程序之间的相似点和差异以及实现它的各种方法的问题 但是有一件事开始让我有点困惑 据我搜索 还没有被问到 是关于函数组合和应用程序之间的差异表现 当我学习 F 时 我爱上了管道操作符 gt 这在 haskell