我尝试了很长时间以在haskell中简化此功能,我想举例说明:

mySum x y = x + y
mySum x y = (+) x y
mySum x = (+) x
mySum = (+) -- it's Messi's goal!

我的功能稍微复杂一点,但是我实在做不到,我到处都是,我知道有一些技巧,例如修改右侧,并使用flip。我尝试过,但被困在这里:
zipWith' :: (a -> b -> c) -> [a] -> [b] -> [c]
zipWith' f x y  = map  (uncurry f) (zip x y)

脚步:
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

然后我不知道如何继续...

我正在寻找一个可以逐步解释如何实现“梅西目标”的答案,我知道有很多问题要问,因此我将尽一切努力感谢

最佳答案

zipWith' f x y = map (uncurry f) (zip x y)

将应用程序重写为组合和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

将前缀重写为:
-- g . h = (.) g h

zipWith' f x = (.) (map (uncurry f)) (zip x)

将应用程序重写为组合和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

将前缀重写为:
-- g . h = (.) g h

zipWith' f = (.) ((.) (map (uncurry f))) zip

使用flipf移动到右侧:
-- flip f x y = f y x

zipWith' f = flip (.) zip ((.) (map (uncurry f)))

将应用程序重写为组合:
-- g (h (i x)) = (g . h . i) x

zipWith' f = flip (.) zip (((.) . map . uncurry) f)

将应用程序重写为组合和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)

删除多余的括号:
zipWith' = flip (.) zip . (.) . map . uncurry

并简化为infix(如果您愿意):
zipWith' = (. zip) . (.) . map . uncurry

不过,此结果不太可读。

通常,在编写完全无点的代码时,您想利用->Control.Arrow应用程序和箭头组合器。与其尝试编写像\ f x y -> ...这样的函数,不如从将参数分组为元组开始,以使它们更易于重新排列和遍历。在这种情况下,我将使用\ (f, (x, y)) -> ...
\ (f, (x, y)) -> map (uncurry f) (zip x y)

我们可以通过将(x, y)应用于uncurry来消除zip的拆包:
\ (f, (x, y)) -> map (uncurry f) (uncurry zip (x, y))
\ (f, xy) -> map (uncurry f) (uncurry zip xy)

现在我们有一个简单的案例:将两个函数(uncurryuncurry zip)应用于两个参数(fxy),然后将结果组合在一起(与map)。为此,我们可以使用***中的Control.Arrow组合器,其类型为:
(***) :: Arrow a => a b c -> a b' c' -> a (b, b') (c, c')

专用于功能,即:
(***) @(->) :: (b -> c) -> (b' -> c') -> (b, b') -> (c, c')

这只是让我们将函数应用于一对中的每个元素。完美的!
uncurry *** uncurry zip
  :: (a -> b -> c, ([x], [y])) -> ((a, b) -> c, [(x, y)])

您可以将uncurry f视为使用f函数组合一对元素。因此,在这里我们可以使用uncurry map合并结果:
uncurry map . (uncurry *** uncurry zip)
  :: (a -> b -> c, ([a], [b])) -> [c]

您可以将curry视为将元组上的函数转换为多参数函数。在这里,我们有两个级别的元组,外部(f, xy)和内部(x, y)。我们可以使用curry打开外部包装:
curry $ uncurry map . (uncurry *** uncurry zip)
  :: (a -> b -> c) -> ([a], [b]) -> [c]

现在,您可以将fmap f应用程序中的->视为“跳过”第一个参数:
fmap @((->) _) :: (a -> b) -> (t -> a) -> t -> b

因此,我们可以使用fmap curry打开第二个元组的包装:
fmap curry $ curry $ uncurry map . (uncurry *** uncurry zip)
  :: (a -> b -> c) -> [a] -> [b] -> [c]

我们完成了!还是不完全是。编写无点代码时,需要将其分解为名称更清晰的许多小型可重用函数,例如:
zipWith' = untuple2 $ combineWith map apply zipped
  where
    untuple2 = fmap curry . curry
    combineWith f g h = uncurry f . (g *** h)
    apply = uncurry
    zipped = uncurry zip

但是,尽管知道这些技术是有用的,但所有这些只是徒劳的欺骗,很容易迷失方向。大多数情况下,只有在明显提高可读性的情况下,才应在Haskell中使用无点样式,并且这些结果均不会比简单的原始版本更清晰:
zipWith' f x y = map (uncurry f) (zip x y)

或部分无积分的版本:
zipWith' f = map (uncurry f) .: zip
  where (.:) = (.) . (.)

09-28 14:07