Haskell 中的无点
-
20-09-2019 - |
题
我有这段代码,我想使其成为无点的;
(\k t -> chr $ a + flip mod 26 (ord k + ord t -2*a))
我怎么做?
除了“思考这个并想出一些东西”之外,还有一些关于无点风格的一般规则吗?
解决方案
把一个功能
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
然后我可以取消的 z
s得到
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*一个
- 采取的结果,国防部26
- 增加一个
- 适用
chr
所以作为一种首次尝试在简化,我们会得到:
func k t = chr . (+a) . (`mod` 26) . subtract (2*a) $ ord k + ord t
注意,你可以避免 flip
通过使用一部分 mod
, 和使用的部分 -
变得混乱在Haskell,以便有一个 subtract
功能(他们与冲突的语法书面负数字: (-2)
意味着负2,而不是一样 subtract 2
).
在这一功能, ord k + ord t
是一个优秀的候选人使用 Data.Function.on
(链接).这个有用的组合可以让我们更换 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)
不幸的是哈斯克尔是一位杂乱的时候撰写的二进制的功能有一个序列的一元的职能,但有一招(我会看看我能找到一个很好的参考之用),和我们结束:
import Data.Function (on)
func = ((chr . (+a) . (`mod` 26) . subtract (2*a)) .) . ((+) `on` ord)
这几乎是一个漂亮整洁点-免费功能的管道,除了那个丑陋的构成招。通过定义 .:
操作者建议在评论 在这个页面, 这收拾了一点:
import Data.Function (on)
(.:) = (.).(.)
func = (chr . (+a) . (`mod` 26) . subtract (2*a)) .: ((+) `on` ord)
对波兰语的这些更多,你可以添加一些辅助职能分开的信 <->Int转 凯撒密码 算。例如: letterToInt = subtract a . ord
其他提示
除了“思考这个并想出一些东西”之外,还有一些关于无点风格的一般规则吗?
您始终可以作弊并使用 lambdabot 中的“pl”工具(通过转到 freenode 上的#haskell 或使用例如 葡萄糖酸)。对于您的代码,请给出:
((chr . (a +) . flip mod 26) .) . flip flip (2 * a) . ((-) .) . (. ord) . (+) . ord
如果你问我的话,这并不是真正的进步。
有绝对一套招数转化表达为自由点式。我不宣称自己是专家,但这里有一些提示。
首先,你要给函数的参数中表达的最右边的长期隔离。你的主要工具,在这里将flip
和$
,使用规则:
f a b ==> flip f b a
f (g a) ==> f $ g a
其中f
和g
是函数,和a
和b
是表达式。这样开始:
(\k t -> chr $ a + flip mod 26 (ord k + ord t -2*a))
-- replace parens with ($)
(\k t -> chr $ (a +) . flip mod 26 $ ord k + ord t - 2*a)
-- prefix and flip (-)
(\k t -> chr $ (a +) . flip mod 26 $ flip (-) (2*a) $ ord k + ord t)
-- prefix (+)
(\k t -> chr $ (a +) . flip mod 26 $ flip (-) (2*a) $ (+) (ord k) (ord t))
现在,我们需要得到t
出在右手侧。要做到这一点,使用规则:
f (g a) ==> (f . g) a
和这样:
-- pull the t out on the rhs
(\k t -> chr $ (a +) . flip mod 26 $ flip (-) (2*a) $ ((+) (ord k) . ord) t)
-- flip (.) (using a section)
(\k t -> chr $ (a +) . flip mod 26 $ flip (-) (2*a) $ ((. ord) $ (+) (ord k)) t)
-- pull the k out
(\k t -> chr $ (a +) . flip mod 26 $ flip (-) (2*a) $ ((. ord) . ((+) . ord)) k t)
现在,我们需要把一切k
和t
左成一个大的函数项,所以我们有形式(\k t -> f k t)
的表达。这就是事情变得有点令人费解。首先,请注意,所有的条款,直至最后$
与一个参数的功能,所以我们可以撰写他们:
(\k t -> chr . (a +) . flip mod 26 . flip (-) (2*a) $ ((. ord) . ((+) . ord)) k t)
现在,我们有我们希望与类型Char -> Char -> Int
的函数组成,得到类型Int -> Char
的函数类型Char -> Char -> Char
的功能。我们可以实现,使用(非常古怪的)规则
f (g a b) ==> ((f .) . g) a b
这为我们提供了:
(\k t -> (((chr . (a +) . flip mod 26 . flip (-) (2*a)) .) . ((. ord) . ((+) . ord))) k t)
现在我们可以只申请一个beta减少:
((chr . (a +) . flip mod 26) .) . (flip flip (2*a) . ((-) . ) . ((. ord) . (+) .ord))
我假设你的点释放的点是使代码更简洁,更具有可读性。因此,我认为这是明智的,也是这样做对简化一些其他的重构,然后可能更容易去除的变量。
(\k t -> chr $ a + flip mod 26 (ord k + ord t - 2*a))
所有的第一,flip
是不必要的:
(\k t -> chr $ a + (ord k + ord t - 2*a) `mod` 26)
接着,我会用名和治以分解出独立可用的子功能:
encode_characters k t = chr $ encode (ord k) (ord t)
encode x y = (x + y - 2*a) `mod` 26 + a
我也给一个名称第一表达使其更清晰和可重复使用的。 encode_characters
现在很容易使用该技术从@Nefrubyr使自由点:
encode_characters = chr . encode `on` ord
至于第二个表达式,我不能产生一个形式,这比在其他的答案中所示的任何更好的可读性和他们都高于逐点形式的可读性。因此,我建议停止在这一点上重构和欣赏生成的代码的清洁和可重用性。
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~
PS:作为练习,根据问题的背景下,功能接口的一些轻微的修改(以何种形式什么数据被传递到功能)可以通过一般化问题得到更简化
。一个。实现和简化功能encode_n_characters :: [Char] -> Char
其中encode_characters k t = encode_n_characters [k, t]
。是结果不是专门的两个参数的功能简单?
B中。通过实现定义encode'
功能encode' (x + y) = encode x y
并重新实现encode_characters
使用此功能。是否其中一个功能变得更简单?为实现更简单的整体?被encode'
比encode
或多或少可重复使用的?
连接上 IRC,#haskell, , 和 询问 lambdabot !:
<you> @pl (\k t -> chr $ a + flip mod 26 (ord k + ord t -2*a))
<lambdabot> [the answer]