[英]Eta reduction in haskell
I tried for a long time to reduct this function in haskell, I want to express for example:我试了很久在haskell中减少这个功能,我想表达例如:
mySum x y = x + y
mySum x y = (+) x y
mySum x = (+) x
mySum = (+) -- it's Messi's goal!
My function it a little more complex, but I really can't do it, I was looking out here and there, and I know there are some techniques, like modify the right side, and use flip
.我的功能有点复杂,但我真的做不到,我四处寻找,我知道有一些技巧,比如修改右侧,并使用
flip
。 I tried and I got stuck here:我试过了,我被困在这里:
zipWith' :: (a -> b -> c) -> [a] -> [b] -> [c]
zipWith' f x y = map (uncurry f) (zip x y)
Steps:脚步:
zipWith' f x y = map (uncurry f) (zip x y)
zipWith' f x y = flip map (zip x y) (uncurry f)
zipWith' f x y = flip map (zip x y) $ uncurry f
and then I don't know how to continue...然后就不知道怎么继续了……
I'm looking for an answer that could explain step by step how to achieve the "Messi's goal", I know is a lot to ask, so I will add as soon as I can a bounty to thank the effort我正在寻找一个可以逐步解释如何实现“梅西的目标”的答案,我知道有很多问题要问,所以我会尽快添加赏金以感谢他们的努力
zipWith' f x y = map (uncurry f) (zip x y)
Rewrite application to composition and eta-reduce:将应用程序重写为组合和 eta-reduce:
-- \y -> let g = map (uncurry f); h = zip x in (g . h) y
-- let g = map (uncurry f); h = zip x in g . h
zipWith' f x = map (uncurry f) . zip x
Rewrite infix to prefix:将中缀重写为前缀:
-- g . h = (.) g h
zipWith' f x = (.) (map (uncurry f)) (zip x)
Rewrite application to composition and eta-reduce:将应用程序重写为组合和 eta-reduce:
-- \x -> let g = (.) (map (uncurry f)); h = zip in (g . h) x
-- let g = (.) (map (uncurry f)); h = zip in g . h
zipWith' f = (.) (map (uncurry f)) . zip
Rewrite infix to prefix:将中缀重写为前缀:
-- g . h = (.) g h
zipWith' f = (.) ((.) (map (uncurry f))) zip
Use flip
to move f
to the right-hand side:使用
flip
将f
移到右侧:
-- flip f x y = f y x
zipWith' f = flip (.) zip ((.) (map (uncurry f)))
Rewrite application to composition:将应用程序重写为组合:
-- g (h (i x)) = (g . h . i) x
zipWith' f = flip (.) zip (((.) . map . uncurry) f)
Rewrite application to composition and eta-reduce:将应用程序重写为组合和 eta-reduce:
-- \f -> let g = flip (.) zip; h = (.) . map . uncurry in (g . h) f
-- let g = flip (.) zip; h = (.) . map . uncurry in g . h
zipWith' = (flip (.) zip) . ((.) . map . uncurry)
Remove redundant parentheses:去掉多余的括号:
zipWith' = flip (.) zip . (.) . map . uncurry
And simplify to infix if you like:如果您愿意,可以简化为中缀:
zipWith' = (. zip) . (.) . map . uncurry
This result isn't very readable, though.但是,这个结果不是很可读。
Often when writing fully point-free code, you want to take advantage of the ->
applicative and arrow combinators from Control.Arrow
.通常在编写完全无点代码时,您希望利用
Control.Arrow
中的->
applicative 和箭头组合器。 Rather than trying to write a function like \\ fxy -> ...
, you can start by grouping the arguments into tuples to make them easier to rearrange and pipe around.与其尝试编写像
\\ fxy -> ...
这样的函数,不如\\ fxy -> ...
参数分组到元组中,使它们更容易重新排列和传递。 In this case I'll use \\ (f, (x, y)) -> ...
在这种情况下,我将使用
\\ (f, (x, y)) -> ...
\ (f, (x, y)) -> map (uncurry f) (zip x y)
We can eliminate the unpacking of (x, y)
by applying uncurry
to zip
:我们可以通过将
uncurry
应用于zip
来消除(x, y)
的解包:
\ (f, (x, y)) -> map (uncurry f) (uncurry zip (x, y))
\ (f, xy) -> map (uncurry f) (uncurry zip xy)
Now we have a simple case: applying two functions ( uncurry
and uncurry zip
) to two arguments ( f
and xy
), then combining the results (with map
).现在我们有一个简单的案例:将两个函数(
uncurry
和uncurry zip
)应用于两个参数( f
和xy
),然后组合结果(使用map
)。 For this we can use the ***
combinator from Control.Arrow
, of type:为此,我们可以使用
Control.Arrow
的***
组合器,类型为:
(***) :: Arrow a => a b c -> a b' c' -> a (b, b') (c, c')
Specialised to functions, that's:专门用于功能,即:
(***) @(->) :: (b -> c) -> (b' -> c') -> (b, b') -> (c, c')
This just lets us apply a function to each element of a pair.这只是让我们将一个函数应用于一对中的每个元素。 Perfect!
完美的!
uncurry *** uncurry zip
:: (a -> b -> c, ([x], [y])) -> ((a, b) -> c, [(x, y)])
You can think of uncurry f
as combining the elements of a pair using the function f
.您可以将
uncurry f
视为使用函数f
组合一对元素。 So here we can combine the results using uncurry map
:所以在这里我们可以使用
uncurry map
组合结果:
uncurry map . (uncurry *** uncurry zip)
:: (a -> b -> c, ([a], [b])) -> [c]
And you can think of curry
as turning a function on tuples into a multi-argument function.您可以将
curry
视为将元组上的函数转换为多参数函数。 Here we have two levels of tuples, the outer (f, xy)
and the inner (x, y)
.这里我们有两层元组,外层
(f, xy)
和内层(x, y)
。 We can unpack the outer one with curry
:我们可以用
curry
打开外面的:
curry $ uncurry map . (uncurry *** uncurry zip)
:: (a -> b -> c) -> ([a], [b]) -> [c]
Now, you can think of fmap f
in the ->
applicative as “skipping over” the first argument:现在,您可以将
->
applicative 中的fmap f
视为“跳过”第一个参数:
fmap @((->) _) :: (a -> b) -> (t -> a) -> t -> b
So we can unpack the second tuple using fmap curry
:所以我们可以使用
fmap curry
解压第二个元组:
fmap curry $ curry $ uncurry map . (uncurry *** uncurry zip)
:: (a -> b -> c) -> [a] -> [b] -> [c]
And we're done!我们完成了! Or not quite.
或者不完全。 When writing point-free code, it pays to break things out into many small reusable functions with clearer names, for example:
在编写无点代码时,将事情分解成许多具有更清晰名称的小型可重用函数是值得的,例如:
zipWith' = untuple2 $ combineWith map apply zipped
where
untuple2 = fmap curry . curry
combineWith f g h = uncurry f . (g *** h)
apply = uncurry
zipped = uncurry zip
However, while knowing these techniques is useful, all this is just unproductive trickery that's easy to get lost in. Most of the time, you should only use point-free style in Haskell when it's a clear win for readability, and neither of these results is clearer than the simple original version:然而,虽然知道这些技术是有用的,但所有这些都只是容易迷失的徒劳技巧。 大多数时候,你应该只在 Haskell 中使用无点风格,当它明显赢得可读性时,这些结果都没有比简单的原始版本更清晰:
zipWith' f x y = map (uncurry f) (zip x y)
Or a partially point-free version:或者部分免积分版本:
zipWith' f = map (uncurry f) .: zip
where (.:) = (.) . (.)
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.