Haskell 笛卡尔积,带过滤器的 Monad
Haskell Cartesian Product, Monad with filter
这必须非常简单,我很不高兴,根据我的 Haskell 经验,我目前无法弄清楚。我想要一个列表的笛卡尔积,但我想过滤掉相同的项目。我不想要 post 过滤器。
这让我得到了 CP - 似乎设置为简单地添加一个过滤器...
p as bs = do
a <- as
b <- bs
return (a,b)
p [1,2,3] [1,2,3]
[(1,1),(1,2),(1,3),(2,1),(2,2),(2,3),(3,1),(3,2),(3,3)]
我读到 return ()
基本上是 do 符号中的一个 noop - 但这不会编译。 (元组是否混淆)
pf as bs = do
a <- as
b <- bs
if a == b then return () else return (a,b)
* Couldn't match type `()' with `(a, a)'
Expected type: [(a, a)]
Actual type: [()]
我尝试了其他一些东西,例如 Haskell wiki 中的 if'
函数。我也试过 when
没有成功。当过滤器为 when
when (a /= b) return (a,b)
* Couldn't match type `m0 (a, a)' with `()'
Expected type: (a, a) -> ()
Actual type: (a, a) -> m0 (a, a)
我想这些错误信息让我不知所措,但我还不擅长翻译其中的大部分。
很可能有一个更高级别的函数可以更直接地处理这个问题(filterM?),我很高兴听到它的用法,但我仍然想知道如何解决这个问题上述 pf
函数中的问题。
谢谢
尝试:
main = print $ p [1,2,3] [1,2,3] -- [(1,2),(1,3),(2,1),(2,3),(3,1),(3,2)]
p as bs =
do
a <- as
b <- bs
if a == b then [] else return (a, b)
我最近才学 Haskell 一点,所以我可能是错的。在这种情况下,return (a, b)
只不过是表达式 [(a, b)]
(这很自然,因为对于 monad []
你需要一个输出类型为 [t]
的函数)。所以你需要提供相同的类型,即空列表 []
.
另一方面,当你写 return ()
时,它实际上是 [()]
,其类型是 [()]
,这与类型 [(Int, Int)]
不同].
您也可以使用 Control.Monad.guard
而不是直接使用 []
。
import Control.Monad (guard)
p as bs =
do
a <- as
b <- bs
guard $ a /= b
return (a, b)
您可以将其视为列表理解 [(a, b) | a <- as, b <- bs, a /= b]
.
的 do
表示法版本
你可以修复你的
pf as bs = do
a <- as
b <- bs
if a == b then return () else return (a,b)
将其更改为
pf2 :: (Monad m, Eq t) => m t -> m t -> m [(t, t)]
pf2 as bs = do
a <- as
b <- bs
if a == b then return [] else return [(a,b)]
如果 m
是 []
类型,这会得到我们
pf3 :: (Eq t) => [t] -> [t] -> [ [(t, t)] ]
pf3 as bs = do
a <- as
b <- bs
if a == b then return [] else return [(a,b)]
所以它现在确实通过了 type-checker,但是我们在我们想要的类型周围创建了一个额外的 []
层:
> pf3 [1,2,3] [1,2,3] :: [ [(Int,Int)] ]
[[],[(1,2)],[(1,3)], [(2,1)],[],[(2,3)], [(3,1)],[(3,2)],[]]
所以这几乎就是我们想要的,除了一些额外的 []
s 为了更好地衡量。如果我们能让它们消失就好了,
> concat it
[ (1,2), (1,3), (2,1), (2,3), (3,1), (3,2) ]
我们会得到我们真正想要的最终结果。
正如您在上面看到的那样,我们通过将 pf3
的输出传递给 concat
,确实获得了最终所需的值。但是concat
是[]
-type-monad的join
:
> :t concat :: [] ([] a) -> [] a
concat :: [] ([] a) -> [] a :: [[a]] -> [a]
> :t join :: [] ([] a) -> [] a
join :: [] ([] a) -> [] a :: [[a]] -> [a]
> :t join
join :: Monad m => m (m a) -> m a
定义为
join xs = do = do
{ x <- xs { x <- xs
; x ; y <- x
} ; return y
}
因此我们上面写的融合在一起成为
pf4 as bs = join $ do
a <- as
b <- bs
if a == b then return [] else return [(a,b)]
= do {
x <- do { a <- as
; b <- bs
; if a == b then return [] else return [(a,b)] }
; x
}
= do { a <- as
; b <- bs
; x <- if a == b then return [] else return [(a,b)]
; x
}
= do { a <- as
; b <- bs
; let { x = if a == b then [] else [(a,b)] }
; x
}
= do { a <- as
; b <- bs
; if a == b then [] else [(a,b)]
}
= do { a <- as
; b <- bs
; if a == b then [] else [()]
; return (a,b)
}
你喜欢最后两个中的哪个。两者中的第一个是您通常手写的内容,最后一个对应于使用 reject (a==b)
作为第三行 do
,定义为
reject :: MonadPlus m => Bool -> m ()
reject True = mzero -- mzero :: MonadPlus m => m a
reject False = return () -- () to be ignored
对于 []
类型的 monad,mzero = [] :: [] a
(当然还有 return x = [x] :: [] a
)。
刚好reject b = guard (not b)
,与built-in guard
.
旁注:
return ()
是 no-op 在某种意义上
do { a <- as ; b <- bs ; return (a,b) } ==
do { return () ; a <- as ; b <- bs ; return (a,b) } ==
do { a <- as ; return () ; b <- bs ; return (a,b) } ==
do { a <- as ; b <- bs ; return () ; return (a,b) }
你可以把它放在 do
块的任何一行,除了最后一行,它不会改变任何东西。 any monad 也是如此。
这必须非常简单,我很不高兴,根据我的 Haskell 经验,我目前无法弄清楚。我想要一个列表的笛卡尔积,但我想过滤掉相同的项目。我不想要 post 过滤器。
这让我得到了 CP - 似乎设置为简单地添加一个过滤器...
p as bs = do
a <- as
b <- bs
return (a,b)
p [1,2,3] [1,2,3]
[(1,1),(1,2),(1,3),(2,1),(2,2),(2,3),(3,1),(3,2),(3,3)]
我读到 return ()
基本上是 do 符号中的一个 noop - 但这不会编译。 (元组是否混淆)
pf as bs = do
a <- as
b <- bs
if a == b then return () else return (a,b)
* Couldn't match type `()' with `(a, a)'
Expected type: [(a, a)]
Actual type: [()]
我尝试了其他一些东西,例如 Haskell wiki 中的 if'
函数。我也试过 when
没有成功。当过滤器为 when
when (a /= b) return (a,b)
* Couldn't match type `m0 (a, a)' with `()'
Expected type: (a, a) -> ()
Actual type: (a, a) -> m0 (a, a)
我想这些错误信息让我不知所措,但我还不擅长翻译其中的大部分。
很可能有一个更高级别的函数可以更直接地处理这个问题(filterM?),我很高兴听到它的用法,但我仍然想知道如何解决这个问题上述 pf
函数中的问题。
谢谢
尝试:
main = print $ p [1,2,3] [1,2,3] -- [(1,2),(1,3),(2,1),(2,3),(3,1),(3,2)]
p as bs =
do
a <- as
b <- bs
if a == b then [] else return (a, b)
我最近才学 Haskell 一点,所以我可能是错的。在这种情况下,return (a, b)
只不过是表达式 [(a, b)]
(这很自然,因为对于 monad []
你需要一个输出类型为 [t]
的函数)。所以你需要提供相同的类型,即空列表 []
.
另一方面,当你写 return ()
时,它实际上是 [()]
,其类型是 [()]
,这与类型 [(Int, Int)]
不同].
您也可以使用 Control.Monad.guard
而不是直接使用 []
。
import Control.Monad (guard)
p as bs =
do
a <- as
b <- bs
guard $ a /= b
return (a, b)
您可以将其视为列表理解 [(a, b) | a <- as, b <- bs, a /= b]
.
do
表示法版本
你可以修复你的
pf as bs = do
a <- as
b <- bs
if a == b then return () else return (a,b)
将其更改为
pf2 :: (Monad m, Eq t) => m t -> m t -> m [(t, t)]
pf2 as bs = do
a <- as
b <- bs
if a == b then return [] else return [(a,b)]
如果 m
是 []
类型,这会得到我们
pf3 :: (Eq t) => [t] -> [t] -> [ [(t, t)] ]
pf3 as bs = do
a <- as
b <- bs
if a == b then return [] else return [(a,b)]
所以它现在确实通过了 type-checker,但是我们在我们想要的类型周围创建了一个额外的 []
层:
> pf3 [1,2,3] [1,2,3] :: [ [(Int,Int)] ]
[[],[(1,2)],[(1,3)], [(2,1)],[],[(2,3)], [(3,1)],[(3,2)],[]]
所以这几乎就是我们想要的,除了一些额外的 []
s 为了更好地衡量。如果我们能让它们消失就好了,
> concat it
[ (1,2), (1,3), (2,1), (2,3), (3,1), (3,2) ]
我们会得到我们真正想要的最终结果。
正如您在上面看到的那样,我们通过将 pf3
的输出传递给 concat
,确实获得了最终所需的值。但是concat
是[]
-type-monad的join
:
> :t concat :: [] ([] a) -> [] a
concat :: [] ([] a) -> [] a :: [[a]] -> [a]
> :t join :: [] ([] a) -> [] a
join :: [] ([] a) -> [] a :: [[a]] -> [a]
> :t join
join :: Monad m => m (m a) -> m a
定义为
join xs = do = do
{ x <- xs { x <- xs
; x ; y <- x
} ; return y
}
因此我们上面写的融合在一起成为
pf4 as bs = join $ do
a <- as
b <- bs
if a == b then return [] else return [(a,b)]
= do {
x <- do { a <- as
; b <- bs
; if a == b then return [] else return [(a,b)] }
; x
}
= do { a <- as
; b <- bs
; x <- if a == b then return [] else return [(a,b)]
; x
}
= do { a <- as
; b <- bs
; let { x = if a == b then [] else [(a,b)] }
; x
}
= do { a <- as
; b <- bs
; if a == b then [] else [(a,b)]
}
= do { a <- as
; b <- bs
; if a == b then [] else [()]
; return (a,b)
}
你喜欢最后两个中的哪个。两者中的第一个是您通常手写的内容,最后一个对应于使用 reject (a==b)
作为第三行 do
,定义为
reject :: MonadPlus m => Bool -> m ()
reject True = mzero -- mzero :: MonadPlus m => m a
reject False = return () -- () to be ignored
对于 []
类型的 monad,mzero = [] :: [] a
(当然还有 return x = [x] :: [] a
)。
刚好reject b = guard (not b)
,与built-in guard
.
旁注:
return ()
是 no-op 在某种意义上
do { a <- as ; b <- bs ; return (a,b) } ==
do { return () ; a <- as ; b <- bs ; return (a,b) } ==
do { a <- as ; return () ; b <- bs ; return (a,b) } ==
do { a <- as ; b <- bs ; return () ; return (a,b) }
你可以把它放在 do
块的任何一行,除了最后一行,它不会改变任何东西。 any monad 也是如此。