12

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

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

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

f = (+) 5 $ (/) 8

但是当我在 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.

我不明白“没有实例...”消息。我需要做什么才能以无点样式编写此函数?

4

4 回答 4

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/)

最后一个表达式是有道理的:f 是两个操作的组合,首先将 8 除以一个具有的,然后将结果加 5。请记住,g.h意思是“应用 h,然后应用 g 的结果”。

于 2011-12-11T16:39:20.787 回答
12

从 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)

Eta-reduction 不是完成翻译所必需的,但没有它我们会得到一些更混乱的东西。例如,最后一步会产生((+) 5) . ((/) 8) . id

于 2011-12-11T23:01:17.763 回答
11

“pointfree”程序可以与 一起安装cabal install pointfree,并向您展示如何以 pointfree 样式编写表达式。例如:

$ pointfree "f x = 5 + 8/x"
f = (5 +) . (8 /)

此转换的说明:

  1. 您可以将“部分”用于中缀/运算符功能。(a +) == \b -> a + b(+ a) == \b -> b + a
  2. .函数获取第二个参数的结果,这是一个单参数函数,并将其应用于第一个参数。
于 2011-12-11T16:43:15.177 回答
6

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

f x = (+) 5 $ (/) 8 $ x

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

每当你有一个用 分隔的函数链时,你可以用含义$替换除了最右边的所有函数,如果你有,这相当于。.a $ b $ c $ da . b . c $ d

f x = (+) 5 . (/) 8 $ x

此时,让我们实际删除$括号。

f x = ((+) 5 . (/) 8) x

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

f = (+) 5 . (/) 8

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

<DanBurton> @pl let f x = 5 + 8 / x in f
<lambdabot> (5 +) . (8 /)
于 2011-12-11T22:56:55.447 回答