其他答案显示了如何从头开始惯用地执行此操作,我非常喜欢。展示你如何打磨你已经拥有的东西也可能很有趣。这里再次提醒一下:
concatsplit a = concatMap (\(x,y)-> concatsplit' (x,y)) a
concatsplit' y = map (\x -> ((fst y),x)) (snd y)
我要始终更改的第一件事称为“减少 eta”,即当您将形状 \x -> foo x 的东西变成 foo 时。我们可以在 concatMap 的参数中这样做来获取
concatsplit a = concatMap concatsplit' a
然后在concatsplit 的参数中再次得到:
concatsplit = concatMap concatsplit'
查看concatsplit',我最不喜欢的是使用fst 和snd 而不是模式匹配。使用模式匹配,它看起来像这样:
concatsplit' (a,bs) = map (\x -> (a,x)) bs
如果你真的想练习减少 eta,你可能会注意到 (,) 可以应用前缀并将其更改为
concatsplit' (a,bs) = map (\x -> (,) a x) bs
= map ((,) a) bs
但我想我也一样快乐。在这一点上,这个定义足够小,我很想将它内联到 concatsplit 本身,得到:
concatsplit = concatMap (\(a,bs) -> map ((,) a) bs)
这对我来说似乎是一个很好的定义,我会停在那里。
您可能被这里的几乎 eta-reduction 所困扰:删除bs 几乎是正确的形状。高级用户可能会继续,注意到:
uncurry (\a bs -> map ((,) a) bs) = \(a,bs) -> map ((,) a) bs
因此,通过一些 eta 缩减和其他无点技术,我们可以通过这种方式进行转换:
concatsplit = concatMap (uncurry (\a bs -> map ((,) a) bs))
= concatMap (uncurry (\a -> map ((,) a)))
= concatMap (uncurry (map . (,)))
但是,就我个人而言,我发现这比我在上面声明要停止的地方可读性差,即:
concatsplit = concatMap (\(a,bs) -> map ((,) a) bs)