我有这段代码,希望能够变成点分式;
(\k t -> chr $ a + flip mod 26 (ord k + ord t -2*a))
我该怎么做呢?
此外,除了“考虑这个并想出一些东西”之外,是否有一些点分式样式的通用规则?
我有这段代码,希望能够变成点分式;
(\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
,得到
func x y = (some function pipeline built using x and y)
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
mod
部分上使用一个部分来避免flip
,并且在使用-
的部分中,Haskell会变得混乱,因此有一个subtract
函数(它们与写负数的语法冲突:(-2)
表示负2,并且与subtract 2
不同)。ord k + ord t
非常适合使用Data.Function.on
(link)。 这个有用的组合器让我们用应用于k
和t
的函数替换ord k + ord 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)
letterToInt = subtract a . ord
除了“思考并得出一些东西”之外,是否有一些关于“无参式风格”的普遍规则呢?
你总是可以使用 lambdabot 中的“pl”工具来作弊(可以通过在 Freenode 上进入 #haskell 或者使用例如“ghci on acid”)。输入您的代码后,pl 将给出以下结果:
((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
我还给第一个表达式起了一个名字,以使其更清晰、可重用。使用@Nefrubyr的技巧可以轻松地将encode_characters
变为无参数形式:
encode_characters = chr . encode `on` 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)
((chr . (a +) . flip mod 26) .) . (flip flip (2*a) . ((-) . ) . ((. ord) . (+) .ord))
->
,Monad、Applicative 或 Arrow 的实例也是很不错的技巧。 - ephemientf (g a) ==> f $ g a
并没有真正帮助到你。右边仍然是 f $ (g a)
。你需要的是函数组合。f (g a)
可以写成 (f . g) a
。 - Prateek在IRC,#haskell上连接,并询问lambdabot!:
<you> @pl (\k t -> chr $ a + flip mod 26 (ord k + ord t -2*a))
<lambdabot> [the answer]