减少haskell的Eta

Dam*_*ero 8 haskell reduction

我试了很长时间来减少haskell中的这个功能,我想举例如:

mySum x y = x + y
mySum x y = (+) x y
mySum x = (+) x
mySum = (+) -- it's Messi's goal! 
Run Code Online (Sandbox Code Playgroud)

我的功能有点复杂,但我真的不能这样做,我一直在寻找,我知道有一些技术,比如修改右侧,并使用flip.我试过了,我在这里堆积:

zipWith' :: (a -> b -> c) -> [a] -> [b] -> [c]
zipWith' f x y  = map  (uncurry f) (zip x y) 
Run Code Online (Sandbox Code Playgroud)

脚步:

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
Run Code Online (Sandbox Code Playgroud)

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

我正在寻找一个可以逐步解释如何实现"梅西的目标"的答案,我知道要提出很多问题,所以我会尽快补充,以感谢我的努力

Jon*_*rdy 14

zipWith' f x y = map (uncurry f) (zip x y)
Run Code Online (Sandbox Code Playgroud)

重写应用程序到组合和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
Run Code Online (Sandbox Code Playgroud)

重写中缀为前缀:

-- g . h = (.) g h

zipWith' f x = (.) (map (uncurry f)) (zip x)
Run Code Online (Sandbox Code Playgroud)

重写应用程序到组合和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
Run Code Online (Sandbox Code Playgroud)

重写中缀为前缀:

-- g . h = (.) g h

zipWith' f = (.) ((.) (map (uncurry f))) zip
Run Code Online (Sandbox Code Playgroud)

使用flip移动f到右侧:

-- flip f x y = f y x

zipWith' f = flip (.) zip ((.) (map (uncurry f)))
Run Code Online (Sandbox Code Playgroud)

重写应用程序到组合:

-- g (h (i x)) = (g . h . i) x

zipWith' f = flip (.) zip (((.) . map . uncurry) f)
Run Code Online (Sandbox Code Playgroud)

重写应用程序到组合和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)
Run Code Online (Sandbox Code Playgroud)

删除多余的括号:

zipWith' = flip (.) zip . (.) . map . uncurry
Run Code Online (Sandbox Code Playgroud)

如果您愿意,可以简化中缀:

zipWith' = (. zip) . (.) . map . uncurry
Run Code Online (Sandbox Code Playgroud)

但是,这个结果不是很易读.


通常在编写完全无点代码时,您希望利用来自的->应用程序和箭头组合器Control.Arrow.\ f x y -> ...您可以先将参数分组为元组,以便更容易重新排列和管道,而不是尝试编写类似的函数.在这种情况下,我会使用\ (f, (x, y)) -> ...

\ (f, (x, y)) -> map (uncurry f) (zip x y)
Run Code Online (Sandbox Code Playgroud)

我们可以消除的拆包(x, y)应用uncurryzip:

\ (f, (x, y)) -> map (uncurry f) (uncurry zip (x, y))
\ (f, xy) -> map (uncurry f) (uncurry zip xy)
Run Code Online (Sandbox Code Playgroud)

现在我们有一个简单的例子:将两个函数(uncurryuncurry zip)应用于两个参数(fxy),然后组合结果(with map).为此,我们可以使用以下类型的***组合Control.Arrow器:

(***) :: Arrow a => a b c -> a b' c' -> a (b, b') (c, c')
Run Code Online (Sandbox Code Playgroud)

专门针对功能,即:

(***) @(->) :: (b -> c) -> (b' -> c') -> (b, b') -> (c, c')
Run Code Online (Sandbox Code Playgroud)

这只是让我们将函数应用于一对中的每个元素.完善!

uncurry *** uncurry zip
  :: (a -> b -> c, ([x], [y])) -> ((a, b) -> c, [(x, y)])
Run Code Online (Sandbox Code Playgroud)

你可以认为uncurry f使用函数组合一对元素f.所以我们可以在这里结合使用uncurry map:

uncurry map . (uncurry *** uncurry zip)
  :: (a -> b -> c, ([a], [b])) -> [c]
Run Code Online (Sandbox Code Playgroud)

您可以curry将其视为将元组函数转换为多参数函数.这里我们有两个级别的元组,外部(f, xy)和内部(x, y).我们可以打开外面的包装curry:

curry $ uncurry map . (uncurry *** uncurry zip)
  :: (a -> b -> c) -> ([a], [b]) -> [c]
Run Code Online (Sandbox Code Playgroud)

现在,您可以fmap f->应用程序视为"跳过"第一个参数:

fmap @((->) _) :: (a -> b) -> (t -> a) -> t -> b
Run Code Online (Sandbox Code Playgroud)

所以我们可以使用fmap curry以下方法解压缩第二个元组:

fmap curry $ curry $ uncurry map . (uncurry *** uncurry zip)
  :: (a -> b -> c) -> [a] -> [b] -> [c]
Run Code Online (Sandbox Code Playgroud)

我们完成了!或者不完全.在编写无点代码时,可以将事情分解为许多具有更清晰名称的小型可重用函数,例如:

zipWith' = untuple2 $ combineWith map apply zipped
  where
    untuple2 = fmap curry . curry
    combineWith f g h = uncurry f . (g *** h)
    apply = uncurry
    zipped = uncurry zip
Run Code Online (Sandbox Code Playgroud)

然而,虽然知道这些技术很有用,但所有这些都只是非生产性的伎俩很容易迷失.大多数时候,你应该只在Haskell中使用无点样式才能获得可读性,这些结果都不是比简单的原始版本更清晰:

zipWith' f x y = map (uncurry f) (zip x y)
Run Code Online (Sandbox Code Playgroud)

部分无点版本:

zipWith' f = map (uncurry f) .: zip
  where (.:) = (.) . (.)
Run Code Online (Sandbox Code Playgroud)