haskell - `[ (x !! 0, x !! 1) | x <- mapM (const ['A', 'B', 'C'] ) [1..2], head x < head (tail x) ]` 有效吗?
问题描述
我是 Haskell 的新手,想知道声明如何
[ (x !! 0, x !! 1) | x <- mapM (const ['A', 'B', 'C'] ) [1..2], head x < head (tail x) ]
作品。(我在 StackOverflow 上找到它。)我知道它输出什么,但我并不真正理解它。
解决方案
好吧,上面的表达式可能不是惯用的Haskell。可能更好的版本是:
[ (x0, x1) | (x0:x1:_) <- mapM (const "ABC") [1..2], x0 < x1 ]
这更简洁,如果 中的列表mapM (const "ABC")
将返回一个包含少于两个元素的列表(这在此处是不可能的),则不会出错。
可能这里的核心问题是了解如何mapM
工作。表达式mapM (const "ABC") [1..2]
归结为:
mapM (\_ -> "ABC") [1..2]
因为我们不考虑列表的值,所以它等价于:
replicateM 2 "ABC"
这replicateM
for2
可以重写为(伪 Haskell 语法):
replicateM 2 :: [a] -< [[a]]
replicateM 2 l = do
x0 <- l
x1 <- l
return [x0, x1]
或者如果我们对此进行脱糖:
replicateM 2 l = l >>= \x0 -> l >>= \x1 -> return [x0, x1]
对于列表, 的实例Monad
实现为:
instance Monad [] where
return x = [x]
(>>=) = flip concatMap
所以这意味着对于一个列表 this replicateM 2
,实现为:
replicateM 2 l :: [a] -> [[a]]
replicateM 2 l = concatMap (\x0 -> concatMap (\x1 -> [[x0, x1]]) l) l
或更简单:
replicateM 2 l :: [a] -> [[a]]
replicateM 2 l = concatMap (\x0 -> map (\x1 -> [x0, x1]) l) l
因此,我们对列表中的两项进行所有可能的组合。因此,这意味着:
Prelude Control.Monad> replicateM 2 "ABC"
["AA","AB","AC","BA","BB","BC","CA","CB","CC"]
然后我们在列表推导中使用它,并且对于每个具有两个元素的子列表,我们检查第一个元素x0
是否小于列表推导中带有过滤器部分的第二个元素(x0 < x1
)。如果是这种情况,我们将这些元素生成为 2 元组。
因此"ABC"
,如果第一个元素(严格)小于第二个元素,它会为 中的每两个项目创建 2 个元组。
然而,在这里我们做了一些“太多的工作”:超过一半的元素将被拒绝。我们可以通过使用来优化它tails :: [a] -> [[a]]
:
import Data.List(tails)
[(x0, x1) | (x0:xs) <- tails "ABC", x1 <- xs ]
产生相同的值:
Prelude Control.Monad Data.List> [(x0, x1) | (x0:xs) <- tails "ABC", x1 <- xs ]
[('A','B'),('A','C'),('B','C')]
推荐阅读
- python - 如何在 python 语言中获取此 SQL 查询的输出
- php - 为单个请求的多条记录分配唯一键
- node.js - 从表中选择全部但忽略最后 N 条记录
- svelte - 当任何一组变量发生变化时,Svelte 执行函数
- c# - 如何将 IFromFile 以及其他属性作为集合发布到 .Net core 2.2 Webapi
- html - 额外的普通功能区 css
- branch.io - 分支和 Mixpanel 集成遗留数据
- excel - Excel:在多个常见日期中查找第一次
- bootstrap-4 - Bootstrap 菜单中不显示菜单项
- google-apps-script - 基于自定义模板生成新的电子表格/工作簿