无点风格的简单 Haskell 函数

发布于 2024-12-20 19:06:10 字数 790 浏览 4 评论 0原文

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

fx = 5 + 8/x,我将其重新排列为fx = (+) 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.

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

I am trying to understand how to convert functions to point-free notation in 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:

f x = 5 + 8/x which I rearranged as f x = (+) 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:

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?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

沉睡月亮 2024-12-27 19:06:10

$ 的优先级非常低。因此,fx = (+) 5 $(/) 8 x 实际上意味着 fx = (+) 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。请记住,gh 的意思是“应用 h,然后应用 g 的结果”。

$ has a very low precedence. So, f x = (+) 5 $ (/) 8 x actually means f x = (+) 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. Remember, g.h means "apply h, then apply g the the result of that".

野侃 2024-12-27 19:06:10

从 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 缩减对于完成转换来说不是必需的,但如果没有它,我们会得到一些更混乱的东西。例如,最后一步将产生 ((+) 5) 。 ((/)8) 。 id 代替。

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:

  1. \x -> x translates to id;
  2. \x -> y without x occurring in y translates to const y;
  3. \x -> f g translates to f' <*> g' where
    • f' is a translation of \x -> f and
    • g' is a translation of \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 -> f g translates to const f <*> (\x -> g), which is equal to 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. For example, the last step would yield ((+) 5) . ((/) 8) . id instead.

爱你不解释 2024-12-27 19:06:10

“pointfree”程序可以通过cabal install pointfree安装,并向您展示如何以pointfree风格编写表达式。例如:

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

此转换的说明:

  1. 您可以将“节”用于中缀/运算符函数。 <代码>(a +) == \b -> a + b 和 (+ a) == \b -> b + a
  2. . 函数获取第二个参数(这是一个单参数函数)的结果,并将其应用于第一个参数。

The "pointfree" program can be installed with cabal install pointfree, and shows you how to write an expression in pointfree style. For example:

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

Explanation of this conversion:

  1. You can use "sections" for infix/operator functions. (a +) == \b -> a + b and (+ 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.
梦纸 2024-12-27 19:06:10

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

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

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

每当你有一个由 $ 分隔的函数链时,你可以用 替换除最右边以外的所有函数。 意思是,如果你有 a $ b $ c $ d< /code>,这相当于 a 。 b. c$d

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

此时,我们实际上删除 $ 并改为括号。

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

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

f = (+) 5 . (/) 8

是主要思想。如果您有fx = expr x,您可以将其“eta减少”为f = expr。为了生成无点代码,您只需要简单地认识到较大的函数是如何由较小的函数组成的。对于无点代码,部分应用有时是必要的(在本例中,(+) 5(/) 8 被部分应用)。当你不想思考的时候,“pointfree”程序非常有帮助; #haskell irc 频道上的 Lambdabot 使用该程序作为插件,因此您甚至不必自己安装;只是问:

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

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. The same goes for the expression (/) 8. So you take whatever number is input, x, and first apply the (/) 8 "function", and then apply the (+) 5 "function".

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.

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:

f = (+) 5 . (/) 8

That is the main idea. If you have f x = expr x, you can "eta reduce" it to 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). 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; just ask:

<DanBurton> @pl let f x = 5 + 8 / x in f
<lambdabot> (5 +) . (8 /)
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文