8

我在使用一个函数来删除 Haskell 中的三个参数时遇到了麻烦。

免责声明:不是课程作业,今天有人向我提出了这个问题,这一直困扰着我。

我们得到的自定义类型/功能是(只能记住类型)

type MyThing
  = (Char, String)
type MyThings
  = [MyThing]

funcA :: MyThings -> String -> String
funcB :: MyThings -> String -> Int -> String

我们开始:

funcB as str n = iterate (funcA as) str !! n

并将其减少如下:

funcB as str n = iterate (funcA as) str !! n
funcB as str = (!!) . (iterate (funcA as)) str
funcB as = (!!) . (iterate (funcA as))
funcB as = (!!) . (iterate . funcA) as

然后,卡住了。我们只是不知道如何避免使用最后一个参数。我知道我以前在某个地方看到过类似的情况,并且有一个解决方案。

希望一些 Haskell 天才能指出我为什么是个白痴……

4

3 回答 3

14

您需要的只是运算符部分的以下三个“定律”:

(a `op` b) = (a `op`) b = (`op` b) a = op a b
          (1)          (2)          (3)

使操作数进入运算符附近的空闲槽。

(.)意味着:(a . b) = (a .) b = (. b) a = (.) a b。所以,

f (g x) y !! n      
= (!!) (f (g x) y) n              by (3) 
= ((!!) . f (g x)) y n
= ((!!) . (f . g) x) y n
= ((!!) .) ((f . g) x) y n        by (1)
= (((!!) .) . (f . g)) x y n
= (((!!) .) . f . g) x y n

你应该只做你觉得舒服的无点转换,这样得到的表达式对你来说仍然是可读的——事实上,比原来的更清晰。“pointfree”工具有时会产生不可读的结果。

停在中间是完全可以的。如果您手动完成它太难了,那么您可能也很难阅读它。

((a .) . b) x y = (a .) (b x) y = (a . b x) y = a (b x y)是一种常见的模式,您将很快学会立即识别。所以上面的表达式可以很容易地读回

(!!) ((f . g) x y) n = f (g x) y !! n

考虑到这(.)是关联的:

(a . b . c) = ((a . b) . c) = (a . (b . c))
于 2012-10-30T20:25:32.117 回答
11
funcB = ((!!) .) . iterate . funcA

我想你做了所有的努力,只剩下一小步了。

您确实可以使用pointfree自动执行此操作。请参阅HaskellWiki 页面

正如它在github 自述文件中所说,一旦你安装了它,你可以编辑你的ghci.confor.ghci文件

:def pf \str -> return $ ":! pointfree \"" ++ str ++ "\""

然后在 ghci 中键入

:pf funcB as = (!!) . (iterate . funcA) as

甚至

:pf funcB as str n = iterate (funcA as) str !! n

你得到

funcB = ((!!) .) . iterate . funcA
于 2012-10-30T13:27:46.233 回答
5

对我来说,关键的观察是中缀运算符可以写成前缀:

funcB as = (!!) . (iterate . funcA) as
funcB as = (.) (!!) ((iterate . funcA) as)

一旦你到达这里,你就有一半的机会认识到这是一个组合,(.) (!!)作为第一个参数和iterate . funcA第二个参数:

funcB as = (  ((.) (!!)) . (iterate . funcA)  ) as

现在很清楚如何简化这一点;在那之后,关于如何编写它有很多审美选择。例如,我们可能会观察到它(.)是关联的,因此我们可以去掉一些括号;((.) (!!))同样,如果您认为这样更易读,我们可以使用运算符部分来合并难看的部分。

funcB = (  ((.) (!!)) . (iterate . funcA)  )
funcB = (.) (!!) . iterate . funcA -- uncontroversial parenthesis removal
funcB = ((!!) .) . iterate . funcA -- possibly controversial section rewrite

顺便说一句,我不认为你推导的开头是正确的。您得出了正确的结论,但中间步骤不正确。更正后,它应该如下所示:

funcB as str n = iterate (funcA as) str !! n
funcB as str n = (!!) (iterate (funcA as) str) n
funcB as str = (!!) (iterate (funcA as) str)
funcB as = (!!) . iterate (funcA as)
于 2012-10-30T17:45:22.680 回答