在 Haskell 中对 3 个参数进行柯里化
Posted
技术标签:
【中文标题】在 Haskell 中对 3 个参数进行柯里化【英文标题】:Currying 3 Arguments in Haskell 【发布时间】:2012-10-30 13:23:44 【问题描述】:我在使用柯里化函数来删除 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 天才能指出我为什么是个白痴...
【问题讨论】:
您可以使用pointfree
自动执行此操作。
大呼,会调查的!
【参考方案1】:
您只需要以下三个运算符部分的“定律”:
(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))
【讨论】:
【参考方案2】:funcB = ((!!) .) . iterate . funcA
我认为你做了所有的努力,只剩下一小步了。
您确实可以使用pointfree 自动执行此操作。见HaskellWiki page
正如github readme 中所说,一旦你安装了它,你就可以编辑你的ghci.conf
或.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
【讨论】:
这不是 eta 减少。 Eta 减少从\x -> e x
移动到e
。您给出的转换(从\x -> f (g x)
移动到f . g
)是另外一回事——也许(.)
的增量扩展?
如果你再仔细阅读你会发现我也没有做出你所说的转变!
是的,后来我意识到增量扩展只是你所做的转换的一部分,但它已经超过了五分钟不回收标记!【参考方案3】:
me 的关键观察是中缀运算符可以写成前缀:
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)
【讨论】:
以上是关于在 Haskell 中对 3 个参数进行柯里化的主要内容,如果未能解决你的问题,请参考以下文章