Haskell pattern matching on vectors
Asked Answered
C

3

7

Is it possible to use list style pattern matching on vectors?

ie

import qualified Data.Vector as V

f :: V.Vector a -> a
f (x:xs) = x 

gives an error

Communicate answered 3/5, 2016 at 0:54 Comment(0)
S
9

-XViewPatterns can let you do this:

{-# 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

Or -XLambdaCase

import Control.Category ((>>>))
-- ...
vsum :: Num a => Vector a -> a
vsum = uncons >>> \case
  Just (a,av) -> a + vsum av
  Nothing     -> 0

But honestly, that's seems like a bit of a code smell as you're using one data structure (Vector) as another ([]) which suggests that maybe your choice of data structure is off.

If you really just want to treat it like a list for the purposes of some algorithm, why not use toList?

Summersummerhouse answered 3/5, 2016 at 1:33 Comment(4)
Or you can use a pattern synonym: pattern x ::: xs <- (uncons -> Just (x, xs))Eboh
Is view pattern still needed no we have guard pattern ?Seeing
I am using functions from the Statistics library, which work on Vectors. So I apparently would have to toList to work over my list then fromList to use the functions? which seems to defeat the point?Communicate
matthias: it depends. Do you want to convert a vector to another vector of the same length? Use fmap. A single value? Use fold. A selection of the elements? Use filter. With both lists and vectors, specialized higher-order functions exist to make various tasks both more efficient and easier for others to read.Summersummerhouse
I
5

@Cactus has pointed out -XPatternSynonym (introduced in 7.8) combined with -XViewPattern can be used to pattern match on vectors. I am here to extend his comment a bit further.

pattern Empty <- (V.null -> True) 

The above defines a pattern synonym Empty for the empty vector. The Empty pattern matches against an empty vector using view pattern (V.null -> True). However, it cannot be used as an expression elsewhere, i.e. a uni-directional synonym, since the system doesn't really know what Empty is as a Vector (we only know that null v is True but there could be other vectors giving True as well).

To remedy this, a where clause can be added specifying that Empty actually is a empty vector, i.e. a bi-directional synonym, as well as a type signature:

pattern Empty :: Vector a
pattern Empty <- (V.null -> True) where Empty = V.empty 

This pattern synonym can be used to define uncons without an if expression:

uncons :: Vector a -> Maybe (a, Vector a)
uncons Empty = Nothing
uncons v     = Just (unsafeHead v, unsafeTail v)

We use uncons to define uni-directional synonym. Note I don't make it bi-directional since cons is costly for vector:

pattern (:<|)  :: a -> Vector a -> Vector a
pattern x :<| xs <- (uncons -> Just (x, xs))

Anyway, we are finally able to pattern match on vectors just like lists:

vsum :: Num a => Vector a -> a 
vsum Empty = 0
vsum (x :<| xs) = x + vsum xs

A complete code is here.

Isocyanide answered 17/12, 2018 at 22:2 Comment(0)
P
1

Vectors aren't intended for that kind of pattern matching--they were created to give Haskell O(1) lists, or lists that can be accessed from any point efficiently.

The closest thing to what you wrote would be something like this:

f v = V.head v

Or, if recursion is what you are looking for, the tail function will get the rest of the list.

But if you are trying to do something that moves along a list like that, there are Vector equivalents of functions such as foldl, find, map, and the like. It depends on what you intend to do.

Pape answered 3/5, 2016 at 1:15 Comment(5)
I am trying to run functions which f :: Vector a -> a on a vector, but I want to move across the vector and recalculate (while keeping the other values) as each new item is added. ie on [1,2,3,4,5,6,7,8,10], i want to [f [1], f [1,2], f [1,2,3]..] and so on.Communicate
@matthias that's a scanSummersummerhouse
@Summersummerhouse sorry, I am bit confused. Isn't a scan a cumulative function that passes a result on to the next iteration? How would I write a scan function for what I have above? I appreciate your help.Communicate
It depends on the definition of f. If there exists g s.t. f [a_0...a_i] = g (f [a_0...a_{i-1}]) a_i forall i and b s.t. f [] = b, then scanl g b [a_0 .. a_n] will yield [ f [], f [a_0], f [a_0,a_1], f [a_0, a_1, a_2] ..., f [a_0, a_1, ... a_n ] ]. Such implementations are often more efficient, as they only need to do O(n) work, rather than O(n^2).Summersummerhouse
If not, then what you need is a version of inits for Data.Vector, which wouldn't be too hard to implement using init. inits [ a_0, a_1 ... a_n ] = [ [], [a_0], [a_0, a_1], [a_0, a_1, a_2], ..., [a_0, a_1, ..., a_n] ], so you could do fmap f . inits.Summersummerhouse

© 2022 - 2024 — McMap. All rights reserved.