17

我有这段代码,我想让它变得无意义;

(\k t -> chr $ a + flip mod 26 (ord k + ord t -2*a))

我怎么做?

除了“想想这个amd想出什么”之外,还有一些关于无点风格的一般规则吗?

4

5 回答 5

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

然后我可以取消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)

对于您的特定功能,请考虑kt经历的流程:

  1. 适用ord于他们每个人
  2. 添加结果
  3. 减去 2*a
  4. 取结果 mod 26
  5. 添加一个
  6. 申请chr

因此,作为简化的第一次尝试,我们得到:

func k t = chr . (+a) . (`mod` 26) . subtract (2*a) $ ord k + ord t

请注意,您可以通过在 Haskell 中使用flipon 的部分mod和使用-get messy 的部分来避免,因此有一个subtract函数(它们与编写负数的语法冲突:(-2)表示负 2,并且与 不同subtract 2)。

在此功能中,是使用( linkord k + ord t )的绝佳候选者。这个有用的组合器让我们可以用一个应用于and的函数来替换:Data.Function.onord k + ord tkt

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

于 2010-03-17T19:00:02.523 回答
10

除了“想想这个amd想出什么”之外,还有一些关于无点风格的一般规则吗?

您可以随时作弊并使用 lambdabot 的“pl”工具(通过转到 freenode 上的 #haskell 或在 acid 上使用例如 ghci)。对于您的代码 pl 给出:

((chr . (a +) . flip mod 26) .) . flip flip (2 * a) . ((-) .) . (. ord) . (+) . ord

如果你问我,这并不是一个真正的改进。

于 2010-03-17T17:33:14.923 回答
3

将表达式转换为无点样式肯定有一套技巧。我不声称自己是专家,但这里有一些提示。

首先,您要隔离表达式最右边的函数参数。您的主要工具将是flipand $,使用以下规则:

f a b ==> flip f b a
f (g a) ==> f $ g a

其中fg是函数,ab是表达式。所以开始:

(\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))
于 2010-03-17T20:29:03.390 回答
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)

接下来,我将使用名称和征服来分解出一个独立可用的子函数:

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] -> Charwhere encode_characters k t = encode_n_characters [k, t]。结果是否比专门的双参数函数更简单?

B. 实现encode'通过定义的函数encode' (x + y) = encode x yencode_characters使用该函数重新实现。任何一个功能都变得更简单了吗?总体上实现是否更简单?encode'更多或更少可重用encode比?

于 2012-04-10T16:55:52.730 回答
0

连接IRC,#haskell询问 lambdabot !

<you> @pl (\k t -> chr $ a + flip mod 26 (ord k + ord t -2*a))
<lambdabot> [the answer]
于 2010-03-17T17:31:16.590 回答