Haskell 向量模式匹配
Haskell pattern matching on vectors
是否可以在向量上使用列表样式模式匹配?
即
import qualified Data.Vector as V
f :: V.Vector a -> a
f (x:xs) = x
报错
向量不是为那种模式匹配而设计的——它们的创建是为了提供 Haskell O(1) 列表,或者可以从任何点有效访问的列表。
最接近你写的是这样的:
f v = V.head v
或者,如果您正在寻找递归,tail
函数将获取列表的其余部分。
但是,如果您正在尝试做一些像这样沿着列表移动的事情,那么有 Vector 等效函数,例如 foldl
、find
、map
等.这取决于你打算做什么。
-XViewPatterns
可以让你这样做:
{-# LANGUAGE ViewPatterns #-}
module VecViewPats where
import Data.Vector (Vector)
import qualified Data.Vector as V
uncons :: Vector a -> Maybe (a, Vector a)
uncons v = if V.null v
then Nothing
else Just (V.unsafeHead v, V.unsafeTail v)
vsum :: Num a => Vector a -> a
vsum (uncons -> Just (a,av)) = a + vsum av
vsum (uncons -> Nothing) = 0
或-XLambdaCase
import Control.Category ((>>>))
-- ...
vsum :: Num a => Vector a -> a
vsum = uncons >>> \case
Just (a,av) -> a + vsum av
Nothing -> 0
但老实说,当您将一种数据结构 (Vector
) 用作另一种数据结构 ([]
) 时,这似乎有点代码味道,这表明您可能选择了数据结构已关闭。
如果出于某种算法的目的,您真的只想将其视为列表,为什么不使用 toList
?
@Cactus 指出 -XPatternSynonym
(在 7.8 中引入)与 -XViewPattern
结合可用于向量的模式匹配。我在这里进一步扩展他的评论。
pattern Empty <- (V.null -> True)
上面为空向量定义了一个模式同义词Empty
。 Empty
模式使用视图模式 (V.null -> True
) 匹配空向量。但是,它不能用作其他地方的表达式,即 uni-directional 同义词,因为系统并不真正知道 Empty
作为 Vector 是什么(我们只知道null v
是 True
但也可能有其他向量给出 True
)。
为了解决这个问题,可以添加一个 where
子句,指定 Empty 实际上是一个空向量,即 bi-directional 同义词,以及类型签名:
pattern Empty :: Vector a
pattern Empty <- (V.null -> True) where Empty = V.empty
此模式同义词可用于定义 uncons
,无需 if 表达式:
uncons :: Vector a -> Maybe (a, Vector a)
uncons Empty = Nothing
uncons v = Just (unsafeHead v, unsafeTail v)
我们使用uncons
来定义单向同义词。请注意,我没有将其设置为双向,因为 cons
对于 vector:
来说成本很高
pattern (:<|) :: a -> Vector a -> Vector a
pattern x :<| xs <- (uncons -> Just (x, xs))
无论如何,我们终于可以像列表一样对向量进行模式匹配了:
vsum :: Num a => Vector a -> a
vsum Empty = 0
vsum (x :<| xs) = x + vsum xs
完整代码为here.
是否可以在向量上使用列表样式模式匹配?
即
import qualified Data.Vector as V
f :: V.Vector a -> a
f (x:xs) = x
报错
向量不是为那种模式匹配而设计的——它们的创建是为了提供 Haskell O(1) 列表,或者可以从任何点有效访问的列表。
最接近你写的是这样的:
f v = V.head v
或者,如果您正在寻找递归,tail
函数将获取列表的其余部分。
但是,如果您正在尝试做一些像这样沿着列表移动的事情,那么有 Vector 等效函数,例如 foldl
、find
、map
等.这取决于你打算做什么。
-XViewPatterns
可以让你这样做:
{-# LANGUAGE ViewPatterns #-}
module VecViewPats where
import Data.Vector (Vector)
import qualified Data.Vector as V
uncons :: Vector a -> Maybe (a, Vector a)
uncons v = if V.null v
then Nothing
else Just (V.unsafeHead v, V.unsafeTail v)
vsum :: Num a => Vector a -> a
vsum (uncons -> Just (a,av)) = a + vsum av
vsum (uncons -> Nothing) = 0
或-XLambdaCase
import Control.Category ((>>>))
-- ...
vsum :: Num a => Vector a -> a
vsum = uncons >>> \case
Just (a,av) -> a + vsum av
Nothing -> 0
但老实说,当您将一种数据结构 (Vector
) 用作另一种数据结构 ([]
) 时,这似乎有点代码味道,这表明您可能选择了数据结构已关闭。
如果出于某种算法的目的,您真的只想将其视为列表,为什么不使用 toList
?
@Cactus 指出 -XPatternSynonym
(在 7.8 中引入)与 -XViewPattern
结合可用于向量的模式匹配。我在这里进一步扩展他的评论。
pattern Empty <- (V.null -> True)
上面为空向量定义了一个模式同义词Empty
。 Empty
模式使用视图模式 (V.null -> True
) 匹配空向量。但是,它不能用作其他地方的表达式,即 uni-directional 同义词,因为系统并不真正知道 Empty
作为 Vector 是什么(我们只知道null v
是 True
但也可能有其他向量给出 True
)。
为了解决这个问题,可以添加一个 where
子句,指定 Empty 实际上是一个空向量,即 bi-directional 同义词,以及类型签名:
pattern Empty :: Vector a
pattern Empty <- (V.null -> True) where Empty = V.empty
此模式同义词可用于定义 uncons
,无需 if 表达式:
uncons :: Vector a -> Maybe (a, Vector a)
uncons Empty = Nothing
uncons v = Just (unsafeHead v, unsafeTail v)
我们使用uncons
来定义单向同义词。请注意,我没有将其设置为双向,因为 cons
对于 vector:
pattern (:<|) :: a -> Vector a -> Vector a
pattern x :<| xs <- (uncons -> Just (x, xs))
无论如何,我们终于可以像列表一样对向量进行模式匹配了:
vsum :: Num a => Vector a -> a
vsum Empty = 0
vsum (x :<| xs) = x + vsum xs
完整代码为here.