简单的Haskell函数采用无点样式

KJ5*_*J50 11 haskell pointfree

我试图了解如何在Haskell中将函数转换为无点表示法.我看到了这个例子,但它比我想要的更复杂.我觉得我理解它背后的逻辑,但当我试图在代码中执行一些简单的例子时,我得到了编译错误.我想尝试以无点样式编写此函数:

f x = 5 + 8/x 我重新安排为 f x = (+) 5 $ (/) 8 x

所以,我认为它可能是这样的:

f = (+) 5 $ (/) 8
Run Code Online (Sandbox Code Playgroud)

但是当我在ghci中运行时,我得到这样的信息:

No instance for (Num (a0 -> a0))
  arising from the literal `5' at Test.hs:3:9
Possible fix: add an instance declaration for (Num (a0 -> a0))
In the first argument of `(+)', namely `5'
In the first argument of `($)', namely `(+) 5'
In the expression: (+) 5 $ (/) 8
Failed, modules loaded: none.
Run Code Online (Sandbox Code Playgroud)

我不明白"没有实例......"的信息.以无点样式编写此函数需要做什么?

Pra*_*eek 17

$优先级很低.所以,f x = (+) 5 $ (/) 8 x实际上意味着f x = (+) 5 $ ((/) 8 x).相反,重写为

f x = (+) 5 ( (/) 8 x)
f x = ((+) 5) ( ((/) 8) x)
f x = ((+) 5) .  ( ((/) 8) ) x
f = ((+) 5) . ( (/) 8 )
f = (5+) . (8/)
Run Code Online (Sandbox Code Playgroud)

最后一个表达式是有意义的:f是两个操作的组合,首先将8除以其中的一个,然后将5添加到结果中.记住,g.h意思是"应用h,然后应用g的结果".


dfl*_*str 11

"pointfree"程序可以安装cabal install pointfree,并向您展示如何以无点样式编写表达式.例如:

$ pointfree "f x = 5 + 8/x"
f = (5 +) . (8 /)
Run Code Online (Sandbox Code Playgroud)

此转换的说明:

  1. 您可以使用"sections"作为中缀/操作符函数.(a +) == \b -> a + b(+ a) == \b -> b + a
  2. .函数获取第二个参数的结果,该参数是单参数函数,并将其应用于第一个参数.

  • @JFritsch - 免费点不是*总是*写一个函数最清晰的方法.但是,在适当的时候,它可以帮助你推理这个函数,因为无点函数只是其他函数的组合. (3认同)

Rot*_*sor 11

从lambda演算(Haskell是其变体)到SKI术语(完全无点函数,仅使用const(K),id(I)和<*>(S))的转换可以使用以下简单规则完成:

  1. \x -> x翻译成id;
  2. \x -> y没有x发生y翻译const y;
  3. \x -> f g转换到f' <*> g'哪里
    • f'是的翻译\x -> f
    • g'是一个翻译\x -> g.

现在你可能想知道它是.从哪里进来的.最后一个翻译有一个特殊情况:如果f没有任何自由出现x,则\x -> f g转换const f <*> (\x -> g)为等于f . (\x -> g).

使用这些规则我们可以转换您的功能:

f = \x -> ((+) 5) (((/) 8) x) = -- by the special-case (.) rule
((+) 5) . (\x -> (((/) 8) x)) = -- by eta-reduction ((\x -> f x) = f)
((+) 5) . ((/) 8)
Run Code Online (Sandbox Code Playgroud)

完成翻译不需要减少Eta,但如果没有它,我们就会变得更加混乱.例如,最后一步将产生((+) 5) . ((/) 8) . id.


Dan*_*ton 5

你真的很亲近 请允许我再添加一个$来说明:

f x = (+) 5 $ (/) 8 $ x
Run Code Online (Sandbox Code Playgroud)

应该清楚的是,表达式(+) 5是一个接受一个数字输入并产生一个数字输出的函数。表达式也是如此(/) 8。因此,无论输入什么数字,您都应x先应用(/) 8“函数”,然后再应用(+) 5“函数”。

只要您有一连串的功能被分隔$,就可以用.Meaning 替换除最右边的所有功能,如果有的话a $ b $ c $ d,则等效于a . b . c $ d

f x = (+) 5 . (/) 8 $ x
Run Code Online (Sandbox Code Playgroud)

在这一点上,我们实际上是除去$和圆括号来代替。

f x = ((+) 5 . (/) 8) x
Run Code Online (Sandbox Code Playgroud)

现在应该很清楚,您可以x从两侧删除尾部:

f = (+) 5 . (/) 8
Run Code Online (Sandbox Code Playgroud)

是主要思想。如果有的话f x = expr x,可以将它“减少”为f = expr。为了产生无点代码,您只需要简单地认识到较大的函数是由较小的函数组成的。部分应用程序有时需要对点自由码(如在这种情况下,(+) 5(/) 8部分地施加)。当您不想考虑时,“ pointfree”程序非常有用。#haskell irc频道上的Lambdabot使用此程序作为插件,因此您甚至不必自己安装它。只是问:

<DanBurton> @pl let f x = 5 + 8 / x in f
<lambdabot> (5 +) . (8 /)
Run Code Online (Sandbox Code Playgroud)