我有这段代码,我想让它变得无意义;
(\k t -> chr $ a + flip mod 26 (ord k + ord t -2*a))
我怎么做?
除了“想想这个amd想出什么”之外,还有一些关于无点风格的一般规则吗?
我有这段代码,我想让它变得无意义;
(\k t -> chr $ a + flip mod 26 (ord k + ord t -2*a))
我怎么做?
除了“想想这个amd想出什么”之外,还有一些关于无点风格的一般规则吗?
开启一个功能
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
于他们每个人chr
因此,作为简化的第一次尝试,我们得到:
func k t = chr . (+a) . (`mod` 26) . subtract (2*a) $ ord k + ord t
请注意,您可以通过在 Haskell 中使用flip
on 的部分mod
和使用-
get messy 的部分来避免,因此有一个subtract
函数(它们与编写负数的语法冲突:(-2)
表示负 2,并且与 不同subtract 2
)。
在此功能中,是使用( linkord k + ord t
)的绝佳候选者。这个有用的组合器让我们可以用一个应用于and的函数来替换: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)
不幸的是,在用一系列一元函数组合二进制函数时,Haskell 有点混乱,但有一个技巧(我会看看是否能找到一个好的参考),我们最终得到:
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
除了“想想这个amd想出什么”之外,还有一些关于无点风格的一般规则吗?
您可以随时作弊并使用 lambdabot 的“pl”工具(通过转到 freenode 上的 #haskell 或在 acid 上使用例如 ghci)。对于您的代码 pl 给出:
((chr . (a +) . flip mod 26) .) . flip flip (2 * a) . ((-) .) . (. ord) . (+) . ord
如果你问我,这并不是一个真正的改进。
将表达式转换为无点样式肯定有一套技巧。我不声称自己是专家,但这里有一些提示。
首先,您要隔离表达式最右边的函数参数。您的主要工具将是flip
and $
,使用以下规则:
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)
现在,我们有一个 type 的函数Char -> Char -> Int
,我们想用一个 type 的函数组合Int -> Char
,产生一个 type 的函数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:作为练习,根据问题的上下文,对函数接口(以何种形式传递给函数的数据)进行一些细微的修改可能会通过概括问题产生更多的简化。
A. 实现和简化功能encode_n_characters :: [Char] -> Char
where 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]