打开一个函数
func x y z = (some expression in x, y and z)
变成无点形式,我一般尽量按照最后一个参数z的做法,把函数写成
func x y z = (some function pipeline built using x and y) z
然后我可以取消zs 来获取
func x y = (some function pipeline built using x and y)
然后对 y 和 x 重复该过程应该以无点形式结束 func。在此过程中要识别的一个基本转换是:
f z = foo $ bar z -- or f z = foo (bar z)
<=> f z = foo . bar $ z
<=> f = foo . bar
同样重要的是要记住,通过部分求值,您可以“中断”函数的最后一个参数:
foo $ bar x y == foo . bar x $ y -- foo applied to ((bar x) applied to y)
对于您的特定功能,请考虑 k 和 t 经历的流程:
- 为每个人申请
ord
- 添加结果
- 减去 2*a
- 取结果 mod 26
- 添加一个
- 申请
chr
因此,作为简化的第一次尝试,我们得到:
func k t = chr . (+a) . (`mod` 26) . subtract (2*a) $ ord k + ord t
请注意,您可以通过在 mod 上使用部分来避免 flip,而使用 - 的部分在 Haskell 中会变得混乱,因此有一个 subtract 函数(它们与写入负数的语法冲突:@987654341 @表示负2,与subtract 2不同。
在此函数中,ord k + ord t 是使用 Data.Function.on (link) 的绝佳候选者。这个有用的组合器让我们可以将ord k + ord t 替换为应用于k 和t 的函数:
func k t = chr . (+a) . (`mod` 26) . subtract (2*a) $ ((+) `on` ord) k t
我们现在非常接近拥有
func k t = (function pipeline) k t
因此
func = (function pipeline)
不幸的是,Haskell 在用一系列一元函数组合二元函数时有点混乱,但有一个技巧(我会看看我是否能找到一个好的参考),我们最终得到:
import Data.Function (on)
func = ((chr . (+a) . (`mod` 26) . subtract (2*a)) .) . ((+) `on` ord)
这几乎是一个很好的整洁的无点函数管道,除了那个丑陋的组合技巧。通过定义 cmets on this page 中建议的 .: 运算符,这可以稍微整理一下:
import Data.Function (on)
(.:) = (.).(.)
func = (chr . (+a) . (`mod` 26) . subtract (2*a)) .: ((+) `on` ord)
为了进一步完善这一点,您可以添加一些辅助函数来将字母 Int 转换与 Caesar cipher 算术分开。例如:letterToInt = subtract a . ord