我有这个代码,我想做无点;
(\k t -> chr $ a + flip mod 26 (ord k + ord t -2*a))
我该怎么做?
除了“想想这个和某些东西”之外,还有一些点免费风格的一般规则吗?
答案 0 :(得分:52)
开启功能
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
来获取
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
请注意,您可以通过使用flip
上的部分来避免使用mod
,而使用-
的部分会在Haskell中变得混乱,因此存在subtract
函数(它们会与编写负数的语法:(-2)
表示否定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)
这几乎是一个很好的简洁无点的功能管道,除了那个丑陋的构图技巧。通过定义评论on this page中建议的.:
运算符,可以稍微整理一下:
import Data.Function (on)
(.:) = (.).(.)
func = (chr . (+a) . (`mod` 26) . subtract (2*a)) .: ((+) `on` ord)
为了更好地完善这一点,您可以添加一些辅助函数来分隔字母&lt; - &gt;来自Caesar cipher算术的Int转换。例如:letterToInt = subtract a . ord
答案 1 :(得分:10)
除了“想想这个和某些东西”之外,还有一些点免费风格的一般规则吗?
你总是可以欺骗并使用lambdabot中的“pl”工具(通过转到freenode上的#haskell或使用例如ghci on acid)。对于您的代码pl给出:
((chr . (a +) . flip mod 26) .) . flip flip (2 * a) . ((-) .) . (. ord) . (+) . ord
如果你问我,那真的不是一个改进。
答案 2 :(得分:3)
将表达式转换为无点样式肯定有一系列技巧。我并不认为自己是专家,但这里有一些提示。
首先,您希望在表达式的最右侧术语中隔离函数参数。您的主要工具将是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))
答案 3 :(得分:3)
我假设你的重点是让代码更简洁,更易读。因此,我认为对简化进行一些其他重构是明智的,这样可以更容易地删除变量。
(\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)
接下来,我将使用 name并征服来分解一个可独立使用的子功能:
encode_characters k t = chr $ encode (ord k) (ord t)
encode x y = (x + y - 2*a) `mod` 26 + a
我还给第一个表达式命名,以使其更清晰和可重用。使用@Nefrubyr:
中的技术,encode_characters
现在很容易实现无点
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
?
答案 4 :(得分:0)
连接IRC, #haskell和ask lambdabot !:
<you> @pl (\k t -> chr $ a + flip mod 26 (ord k + ord t -2*a))
<lambdabot> [the answer]