无点在Haskell [英] Point-free in Haskell
问题描述
我有这个代码,我想要免点;
(\kt - > chr $ a + flip mod 26(ord k + ord t -2 * a))
我如何做?
除了想想这个amd想出什么东西之外,还有一些点自由风格的一般规则?
要打开一个函数
func xyz =(x,y和z中的某个表达式)
为无点形式,我通常尝试按照最后一个参数 z
并将函数写为
func xyz =(使用x和y创建的某些函数管道)z
然后我可以取消 z
get
func xy =(使用x和y创建的某些函数管道)
/ pre>
然后重复这个过程,y和x应该以
func
以无点形式结束。在这个过程中识别的重要转换是:fz = foo $ bar z-或fz = foo(bar z)
< => f z = foo。 bar $ z
< => f = foo。 bar
还有一点要记住,使用部分评估,你可以一个函数:
foo $ bar xy == foo。 bar x $ y - foo应用于((bar x)应用于y)
函数,考虑
k
和t
的流程:
- 将
ord
应用于其中每一个
- 添加结果
- 减去2 * a
- 取结果mod 26
- 添加
- code> chr
作为第一次尝试简化,我们得到:
func kt = chr。 (+ a)。 (`mod` 26)。减去(2 * a)$ ord k + ord t
注意,您可以避免<$ c $通过使用
mod
上的一个部分,使用-
Haskell因此有一个减法
函数(他们与编写负数的语法冲突:( - 2)
表示负2 ,并且与减去2
不同)。
在此函数中,
ord k + ord t
是使用Data.Function.on
( link )。这个有用的组合器让我们用k
和替换
:ord k + ord t
tfunc kt = chr。 (+ a)。 (`mod` 26)。减去(2 * a)$((+)`on` ord)kt
非常接近func kt =(function pipeline)kt
,因此
func =(function pipeline)$ b $不幸的是,Haskell在使用一系列一元函数来构造一个二进制函数时有点混乱,但是有一个一个窍门(我会看看,如果我能找到一个很好的参考),我们最终得到:
运算符=nofollow noreferrer>,这整理了一点:import Data 。function(on)
func =((chr。(+ a)。(`mod` 26)。subtract(2 * a))。 (+)`on` ord)
这几乎是一个不错的无点免费函数管道,除了那个丑陋的伎俩。通过定义。:
import Data.Function on)
(。:) =(。)。(。)
func =(chr。 * a))::((+)`on` ord)
您可以添加一些帮助函数来分隔来自 Caesar密码的字母< - > Int conversion算术。例如:
letterToInt =减去a。 ord
I have this code that I want to make point-free;
(\k t -> chr $ a + flip mod 26 (ord k + ord t -2*a))
How do I do that?
Also are there some general rules for point free style other than "think about this amd come up with something"?
解决方案To turn a function
func x y z = (some expression in x, y and z)
into point-free form, I generally try to follow what is done to the last parameter
z
and write the function asfunc x y z = (some function pipeline built using x and y) z
Then I can cancel out the
z
s to getfunc x y = (some function pipeline built using x and y)
Then repeating the process for y and x should end up with
func
in point-free form. An essential transformation to recognise in this process is:f z = foo $ bar z -- or f z = foo (bar z) <=> f z = foo . bar $ z <=> f = foo . bar
It's also important to remember that with partial evaluation, you can "break off" the last argument to a function:
foo $ bar x y == foo . bar x $ y -- foo applied to ((bar x) applied to y)
For your particular function, consider the flow that
k
andt
go through:
- Apply
ord
to each of them- Add the results
- Subtract 2*a
- Take the result mod 26
- Add a
- Apply
chr
So as a first attempt at simplifying, we get:
func k t = chr . (+a) . (`mod` 26) . subtract (2*a) $ ord k + ord t
Note that you can avoid
flip
by using a section onmod
, and sections using-
get messy in Haskell so there's asubtract
function (they clash with the syntax for writing negative numbers:(-2)
means negative 2, and isn't the same assubtract 2
).In this function,
ord k + ord t
is an excellent candidate for usingData.Function.on
(link). This useful combinator lets us replaceord k + ord t
with a function applied tok
andt
:func k t = chr . (+a) . (`mod` 26) . subtract (2*a) $ ((+) `on` ord) k t
We're now very close to having
func k t = (function pipeline) k t
and hence
func = (function pipeline)
Unfortunately Haskell is a bit messy when it comes to composing a binary function with a sequence of unary functions, but there is a trick (I'll see if I can find a good reference for it), and we end up with:
import Data.Function (on) func = ((chr . (+a) . (`mod` 26) . subtract (2*a)) .) . ((+) `on` ord)
which is almost a nice neat point-free function pipeline, except for that ugly composing trick. By defining the
.:
operator suggested in the comments on this page, this tidies up a little to:import Data.Function (on) (.:) = (.).(.) func = (chr . (+a) . (`mod` 26) . subtract (2*a)) .: ((+) `on` ord)
To polish this some more, you could add some helper functions to separate the letter <-> Int conversion from the Caesar cipher arithmetic. For example:
letterToInt = subtract a . ord
这篇关于无点在Haskell的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!