haskell - Haskell中带有序列大小写的元组列表
问题描述
我正在学习 Haskell 中的 Monads,我正试图从一个函数 (coupleUp3) 一直上升到 ... (coupleUp1) 的最小案例 m,就像我在电脑发烧友的视频中看到的那样 https:// www.youtube.com/watch?v=t1e8gqXLbsU&t=1173s。
但是,我被卡住了,结果总是
[1..3]
就像是:
[(1,1), (2,2), (3,3)]
这不是我想要的,我想要类似的东西
[(1,1),(1,2),(1,3),(2,1),...,(3,3)]
这是其他功能所做的。这是代码,我只是在寻找如何以这种方式实现功能 CoupleUp1 的想法。您可以查看 CoupleUp3 和 CoupleUp2,了解我正在尝试做什么。谢谢!
coupleUp3 :: [a] -> [(a, a)]
coupleUp3 x = do
y <- x
z <- x
return (y, z)
coupleUp2 :: [a] -> [(a, a)]
coupleUp2 x = (x >>= (\y ->
x >>= (\z ->
return (y, z))))
coupleUp1 :: [a] -> [(a, a)]
coupleUp1 x = case x of
[] -> []
(y:ys) -> case ys of
[] -> []
(z:zs) -> (y, z):coupleUp1 ys
解决方案
列出理解和do
符号:
f xs = [(y, z) | y <- xs, z <- xs]
f xs = do
y <- xs
z <- xs
pure (y, z)
对绑定运算符脱糖>>=
:
f xs = xs >>= \ y -> xs >>= \ z -> pure (y, z)
>>=
并且pure
可以为给定的 monad 内联and 的定义:
f xs = concatMap (\ y -> concatMap (\ z -> [(y, z)]) xs) xs
然后分解了一下:
f xs = concat $ map (\ y -> map (\ z -> (y, z)) xs) xs
如果你想更进一步,你可以内联一些简单的concat
and定义map
:
f xs = concat' $ map' (\ y -> map' (\ z -> (y, z)) xs) xs
where
concat' (xs:xss) = xs ++ concat' xss
concat' [] = []
map' f (y:ys) = f y : map' f ys
map' f [] = []
然后一步一步地重写它,简化和内联,直到你最终得到这样的东西:
-- Expanding…
f xs = concat' $ map1 xs
where
concat' [] = []
concat' (xs:xss) = xs ++ concat' xss
-- Previously ”map' (\ y -> …)”.
map1 (y:ys) = map2 y xs : map1 ys
map1 [] = []
-- Previously “map' (\ z -> …)”.
-- (Takes “y” as an explicit argument; previously,
-- it was implicitly captured by the lambda.)
map2 y (z:zs) = (y, z) : map2 y zs
map2 y [] = []
-- Fully expanded.
-- (With renamed functions for clarity.)
f xs = pairWithSelf xs
where
pairWithSelf (y:ys) = pairEachWith y xs ++ pairWithSelf ys
pairWithSelf [] = []
pairEachWith y (z:zs) = (y, z) : pairEachWith y zs
pairEachWith y [] = []
现在,这个定义非常字面地说明了它的作用:“将列表与自身配对 ( f
),对于列表中的每个元素 ( pairWithSelf
),将该元素与列表中的每个元素 ( ) 配对pairEachWith
”。
(++)
inpairWithSelf
来自于内联,而in和concat
递归来自于内联的两个层次。您不能轻易地将其“折叠”为单个非嵌套递归函数,因为该解决方案固有地涉及对列表的嵌套迭代。pairWithSelf
pairEachWith
map
此外,这是一个相当乏味的过程,因为您实际上是在手工完成,象征性地,语言会在优化期间或运行时自动为您做些什么。
因为你有一个递归结构,所以你不能真正让它变得更“原始”。(好吧,您可以使用它fix
来制作本地函数pairWithSelf
和pairEachWith
匿名 lambda,这可能是一个很好的学习练习fix
。)
此外,这个过程与您通常在 Haskell 中的执行方式相反:我们通常希望找到最通用和最高级的实现,尽可能少地使用显式递归。
回到更高的层次,当您看到这种绑定两个独立操作(在本例中为列表)并将结果与纯函数组合的常见模式时:
do
x <- xs
y <- ys
pure (combine x y)
然后你可以用 Applicative 函数替换它。在这里,combine
是对构造函数(,)
,并且xs
和ys
是相同的,所以你可以编写以下任何一个:
f xs = liftA2 (,) xs xs
f xs = (,) <$> xs <*> xs
Applicative 的这种用法在普通的 Haskell 代码中非常常见,无论何时您需要类似“笛卡尔积”的计算(例如配对表或乘法表),或者您想将单子操作的结果传递给纯函数而不显式绑定一个带有绑定语句的<-
名称do
。
推荐阅读
- bash - BASH - 通过文件中的每一行使用 IF 循环(for)
- javascript - 如何使用 html 和 js 在 foreach 循环中为单个按钮创建动态 ID
- c# - 如何在 linq 的动态列表中查找重复项?
- android - 如何在 Android 的 SQLite 数据库的第二个表中插入第一个表 ID?
- google-bigquery - 外部表定义中的 skipLeadingRows=1
- ios - 订阅到期时,Apple 订阅回调不会通知我们(沙盒)
- odbc - Power BI - 如何将 DirectQuery 与 Presto ODBC 结合使用
- ios - 如何解决找不到“glog/logging.h”文件
- authentication - 有没有办法在不使用其 UI 的情况下使用 KeyCloak 身份验证?
- office-js - Word Online 加载项以静默方式无法插入 ContentControl