部分应用函数和currying,如何制作更好的代码而不是很多地图?

Mig*_*lme 5 haskell currying partial-application function-composition map-function

我是Haskell的初学者,我正在努力把握它.

我有以下问题:

我有一个函数,可以获得5个参数

f x y w z a = x - y - w - z - a
Run Code Online (Sandbox Code Playgroud)

我想申请它,而只是改变变量x110然而y,w,z而且a将永远是相同的.我实现的实现如下,但我认为必须有更好的方法.

假设我想使用:

x from 1 to 10
y = 1 
w = 2 
z = 3 
a = 4
Run Code Online (Sandbox Code Playgroud)

据此,我设法应用如下功能:

map ($ 4) $ map ($ 3) $ map ($ 2) $ map ($ 1) (map f [1..10])
Run Code Online (Sandbox Code Playgroud)

我认为必须有更好的方法将大量缺失参数应用于部分应用的函数,而不必使用太多的映射.

pig*_*ker 14

到目前为止,所有的建议都很好.这是另一个,一开始可能看起来有点奇怪,但在许多其他情况下变得非常方便.

一些类型形成运算符,例如[],将一类元素映射到例如Int这些元素的列表类型的运算符[Int],具有存在的性质Applicative.对于名单,这意味着有一些方法,由运营商表示,<*>发音为"应用",把列表的功能,列出的参数到列表的结果.

(<*>) :: [s -> t] -> [s] -> [t]    -- one instance of the general type of <*>
Run Code Online (Sandbox Code Playgroud)

而不是你的普通应用,由空格给出,或者 $

($)   :: (s -> t) ->  s  ->  t
Run Code Online (Sandbox Code Playgroud)

结果是我们可以使用事物列表而不是事物来进行普通的函数式编程:我们有时将其称为"在列表习惯中编程".唯一的另一个因素是,为了应对当我们的某些组件是单独的东西时,我们需要一个额外的小工具

pure :: x -> [x]   -- again, one instance of the general scheme
Run Code Online (Sandbox Code Playgroud)

它将一个东西作为一个列表包装起来,与之兼容<*>.这就是pure将普通价值转化为适用的习语.

对于列表,pure只需创建一个单例列表,并将<*>其中一个函数的每个成对应用的结果生成到其中一个参数中.特别是

pure f <*> [1..10] :: [Int -> Int -> Int -> Int -> Int]
Run Code Online (Sandbox Code Playgroud)

是一个map f [1..10]可以<*>再次使用的函数列表(就像).你的其余论据f并不清楚,所以你需要pure它们.

pure f <*> [1..10] <*> pure 1 <*> pure 2 <*> pure 3 <*> pure 4
Run Code Online (Sandbox Code Playgroud)

对于列表,这给出了

[f] <*> [1..10] <*> [1] <*> [2] <*> [3] <*> [4]
Run Code Online (Sandbox Code Playgroud)

即从[1..10],1,2,3和4中的一个进行申请的方式列表.

开口pure f <*> s是如此常见,它是缩写f <$> s,所以

f <$> [1..10] <*> [1] <*> [2] <*> [3] <*> [4]
Run Code Online (Sandbox Code Playgroud)

通常会写什么.如果你可以过滤掉<$>,pure<*>噪声,它看起来像你心目中的应用.额外的标点符号是必要的,因为Haskell无法区分一堆函数或参数的列表计算与非列表计算单个值但恰好是列表的区别.但是,至少组件按照您开始的顺序排列,因此您可以更轻松地看到正在发生的事情.

秘籍.(1)在我的(不是非常)Haskell的私人方言中,以上就是

(|f [1..10] (|1|) (|2|) (|3|) (|4|)|)
Run Code Online (Sandbox Code Playgroud)

每个成语括号,(|f a1 a2 ... an|)表示纯函数应用于零或多个生活在成语中的参数.这只是一种写作方式

pure f <*> a1 <*> a2 ... <*> an
Run Code Online (Sandbox Code Playgroud)

伊德里斯有成语括号,但哈斯克尔没有添加它们.然而.

(2)在具有代数效应的语言中,非确定性计算的成语与列表的数据类型不同(对于typechecker),尽管您可以轻松地在两者之间进行转换.该计划成为

f (range 1 10) 2 3 4
Run Code Online (Sandbox Code Playgroud)

其中范围不确定地选择给定的下限和上限之间的值.因此,非决定性被视为局部副作用,而不是数据结构,使失败和选择的操作成为可能.您可以在处理程序中包含非确定性计算,这些计算为这些操作赋予意义,并且一个此类处理程序可能会生成所有解决方案的列表.这就是说,额外的符号来解释发生了什么事情是被推到了边界,而不是通过充塞整个室内,像那些<*>pure.

管理事物的界限而不是内部的界限是我们物种设法拥有的为数不多的好主意之一.但至少我们可以一遍又一遍地拥有它.这就是为什么我们农场而不是狩猎.这就是我们更喜欢静态类型检查到动态标签检查的原因.等等...

  • 我见过的最好的应用教程之一! (3认同)

dfe*_*uer 7

其他人已经展示了你可以做到的方法,但我认为展示如何将你的版本转换成更好的东西是有用的.你写了

map ($ 4) $ map ($ 3) $ map ($ 2) $ map ($ 1) (map f [1..10])
Run Code Online (Sandbox Code Playgroud)

map 遵守两个基本法则:

  1. map id = id.也就是说,如果您将身份函数映射到任何列表,您将返回相同的列表.
  2. 对于任何fg,map f . map g = map (f . g).也就是说,使用一个函数映射列表然后另一个函数映射与使用这两个函数的组合映射它.

第二条map法律是我们想在这里适用的法律.

map ($ 4) $ map ($ 3) $ map ($ 2) $ map ($ 1) (map f [1..10])
=
map ($ 4) . map ($ 3) . map ($ 2) . map ($ 1) . map f $ [1..10]
=
map (($ 4) . ($ 3) . ($ 2) . ($ 1) . f) [1..10]
Run Code Online (Sandbox Code Playgroud)

怎么($ a) . ($ b)办?\x -> ($ a) $ ($ b) x = \x -> ($ a) $ x b = \x -> x b a.怎么样($ a) . ($ b) . ($ c)?那是(\x -> x b a) . ($ c) = \y -> (\x -> x b a) $ ($ c) y = \y -> y c b a.现在的模式应该是明确的:($ a) . ($ b) ... ($ y) = \z -> z y x ... c b a.最终,我们得到了

map ((\z -> z 1 2 3 4) . f) [1..10]
=
map (\w -> (\z -> z 1 2 3 4) (f w)) [1..10]
=
map (\w -> f w 1 2 3 4) [1..10]
=
map (\x -> ($ 4) $ ($ 3) $ ($ 2) $ ($ 1) $ f x) [1..10]
Run Code Online (Sandbox Code Playgroud)


Dav*_*vid 5

除了其他的答案说的话,它可能是重新排序函数的参数是一个好主意,尤其x是平时你改变了这样的参数:

f y w z a x = x - y - w - z - a
Run Code Online (Sandbox Code Playgroud)

如果你让这个在x参数最后出现,你可以只写

map (f 1 2 3 4) [1..10]
Run Code Online (Sandbox Code Playgroud)

当然,这在所有情况下都不起作用,但很高兴看到哪些参数更有可能在一系列调用中发生变化,并将它们放在参数列表的末尾,并且参数往往保持相同的开始状态.当你这样做,柯里和部分应用程序通常会帮助你超过他们原本.


Guv*_*nte 4

假设您不介意变量,您只需定义一个接受x并调用f. 如果那里没有函数定义(通常可以使用letor where),则可以使用 lambda 代替。

f' x = f x 1 2 3 4
Run Code Online (Sandbox Code Playgroud)

或者用 lambda

\x -> f x 1 2 3 4
Run Code Online (Sandbox Code Playgroud)