简体   繁体   English

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

[英]simple Haskell functions in point-free style

I am trying to understand how to convert functions to point-free notation in Haskell. 我试图了解如何在Haskell中将函数转换为无点表示法。 I saw this example , but it is more complicated than what I am looking for. 我看到了这个例子 ,但它比我想要的更复杂。 I feel like I understand the logic behind it, but when I am trying to execute some simple examples in code I am getting compile errors. 我觉得我理解它背后的逻辑,但当我试图在代码中执行一些简单的例子时,我得到了编译错误。 I want to try and write this function in point-free style: 我想尝试以无点样式编写此函数:

fx = 5 + 8/x which I rearranged as fx = (+) 5 $ (/) 8 x fx = 5 + 8/x ,我重新排列为fx = (+) 5 $ (/) 8 x

So, I thought it might be something like this: 所以,我认为它可能是这样的:

f = (+) 5 $ (/) 8

but when I run this in ghci I get this message: 但是当我在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.

I don't understand the "No instance for..." message. 我不明白“没有实例......”的信息。 What do I need to do to write this function in point-free style? 以无点样式编写此函数需要做什么?

$ has a very low precedence. $优先级非常低。 So, fx = (+) 5 $ (/) 8 x actually means fx = (+) 5 $ ((/) 8 x) . 所以, fx = (+) 5 $ (/) 8 x实际上意味着fx = (+) 5 $ ((/) 8 x) Instead, rewrite that as 相反,重写为

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

The last expression makes sense: f is the composition of two operations, first divide 8 by what one has, and then add 5 to the result. 最后一个表达式是有意义的:f是两个操作的组合,首先将8除以其中的一个,然后将5添加到结果中。 Remember, gh means "apply h, then apply g the the result of that". 记住, gh意思是“应用h,然后应用g的结果”。

The "pointfree" program can be installed with cabal install pointfree , and shows you how to write an expression in pointfree style. “pointfree”程序可以与cabal install pointfree一起cabal install pointfree ,并向您展示如何以无点样式编写表达式。 For example: 例如:

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

Explanation of this conversion: 此转换的说明:

  1. You can use "sections" for infix/operator functions. 您可以使用“sections”作为中缀/操作符函数。 (a +) == \\b -> a + b and (+ a) == \\b -> b + a (a +) == \\b -> a + b(+ a) == \\b -> b + a
  2. The . . function takes the result of the second parameter, which is a one-argument function, and applies it to the first argument. function获取第二个参数的结果,该参数是单参数函数,并将其应用于第一个参数。

Conversion from lambda-calculus (which Haskell is a variant of) terms to SKI terms (totally pointfree functions, using only const ( K ), id ( I ) and <*> ( S )) can be done with the following simple rules: 从lambda演算(Haskell是其变体)到SKI术语(完全无点函数,仅使用constK ), idI )和<*>S ))的转换可以使用以下简单规则完成:

  1. \\x -> x translates to id ; \\x -> x转换为id ;
  2. \\x -> y without x occurring in y translates to const y ; \\x -> y没有xy转换为const y ;
  3. \\x -> fg translates to f' <*> g' where \\x -> fg转换为f' <*> g'在哪里
    • f' is a translation of \\x -> f and f'\\x -> f和的翻译
    • g' is a translation of \\x -> g . g'\\x -> g的翻译。

Now you may wonder where does the . 现在你可能想知道它在哪里. come in. There is a special case of the last translation: if f does not have any free occurrences of x , then \\x -> fg translates to const f <*> (\\x -> g) , which is equal to f . (\\x -> g) 进来。最后一个翻译有一个特殊情况:如果f没有任何自由出现的x ,那么\\x -> fg转换为const f <*> (\\x -> g) ,它等于f . (\\x -> g) f . (\\x -> g) . f . (\\x -> g)

Using those rules we can convert your function: 使用这些规则我们可以转换您的功能:

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 is not necessary to complete the translation, but without it we'd get something messier. 完成翻译不需要减少Eta,但如果没有它,我们就会变得更加混乱。 For example, the last step would yield ((+) 5) . ((/) 8) . id 例如,最后一步将产生((+) 5) . ((/) 8) . id ((+) 5) . ((/) 8) . id ((+) 5) . ((/) 8) . id instead. ((+) 5) . ((/) 8) . id代替。

You were really close. 你真的很亲密。 Allow me to add one more $ to illustrate: 请允许我再添加一个$来说明:

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

It should be clear that the expression (+) 5 is a function that takes one numeric input and produces a numeric output. 应该清楚的是,表达式(+) 5是一个函数,它接受一个数字输入并产生数字输出。 The same goes for the expression (/) 8 . 表达式(/) 8 So you take whatever number is input, x , and first apply the (/) 8 "function", and then apply the (+) 5 "function". 所以你输入任意数字x ,然后首先应用(/) 8 “函数”,然后应用(+) 5 “函数”。

Whenever you have a chain of functions separated by $ , you can replace all except the rightmost with . 每当你有一个由$分隔的函数链时,你可以替换除最右边的所有函数. Meaning, if you have a $ b $ c $ d , this is equivalent to a . b . c $ d 意思是,如果你有a $ b $ c $ d ,这相当于a . b . c $ d a . b . c $ d a . b . c $ d . a . b . c $ d

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

At this point, let's actually remove the $ and parenthesize instead. 在这一点上,让我们实际上删除 $和括号。

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

Now it should be clear that you can remove the trailing x from both sides: 现在应该清楚的是,你可以从两边删除尾随的x

f = (+) 5 . (/) 8

That is the main idea. 是主要的想法。 If you have fx = expr x , you can "eta reduce" it to f = expr . 如果你有fx = expr x ,你可以“eta reduce”到f = expr In order to produce pointfree code, you need simply recognize how the larger function is composed of smaller functions. 为了生成无点代码,您只需要识别较大的函数如何由较小的函数组成。 Partial application is sometimes necessary for point free code (as in this case, (+) 5 and (/) 8 are partially applied). 对于无点代码,有时需要部分应用(在这种情况下,部分应用(+) 5(/) 8 )。 The "pointfree" program is quite helpful for when you don't want to think about it; 当您不想考虑它时,“免费”程序非常有用; Lambdabot on the #haskell irc channel uses this program as a plugin, so you don't even have to install it yourself; #haskell irc频道上的Lambdabot将此程序用作插件,因此您甚至不必自己安装它; just ask: 请问:

<DanBurton> @pl let f x = 5 + 8 / x in f
<lambdabot> (5 +) . (8 /)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM