Haskell pattern matching conundrum

萝らか妹 提交于 2019-12-11 09:44:33

问题


I was trying to search through a list of pairs that could have the element ("$", Undefined) in it at some arbitrary location. I wanted to ONLY search the part of the list in front of that special element, so I tried something like this (alreadyThere is intended to take the element n and the list xs as arguments):

checkNotSameScope :: Env -> VarName -> Expr -> Expr
checkNotSameScope (xs:("$", Undefined):_) n e = if alreadyThere n xs then BoolLit False
                                                   else BoolLit True

But that does not work; the compiler seemed to indicate that (xs: ..) only deals with a SINGLE value prepending my list. I cannot use : to indicate the first chunk of a list; only a single element. Looking back, this makes sense; otherwise, how would the compiler know what to do? Adding an "s" to something like "x" doesn't magically make multiple elements! But how can I work around this?


回答1:


Unfortunately, even with smart compilers and languages, some programming cannot be avoided...

In your case it seems you want the part of a list up to a specific element. More generally, to find the list up to some condition you can use the standard library takeWhile function. Then you can just run alreadyThere on it:

checkNotSameScope :: Env -> VarName -> Expr -> Expr
checkNotSameScope xs n e = if alreadyThere n (takeWhile (/= ("$", Undefined)) xs)
                           then BoolLit False
                           else BoolLit True

It maybe does not what you want for lists where ("$", Undefined) does not occur, so beware.




回答2:


Similar to Joachim's answer, you can use break, which will allow you to detect when ("$", Undefined) doesn't occur (if this is necessary). i.e.

checkNotSameScope xs n e = case break (== ("$", Undefined)) xs of
                             (_, [])  -> .. -- ("$", Undefined) didn't occur!
                             (xs', _) -> BoolLit . not $ alreadyThere n xs'

(NB. you lose some laziness in this solution, since the list has to be traversed until ("$", Undefined), or to the end, to check the first case.)




回答3:


Haskell cannot do this kind of pattern matching out of the box, although there are some languages which can, like CLIPS for example, or F#, by using active patterns.

But we can use Haskell's existing pattern matching capabilities to obtain a similar result. Let us first define a function called deconstruct defined like this:

deconstruct :: [a] -> [([a], a, [a])]
deconstruct [] = []
deconstruct [x] = [([], x, [])]
deconstruct (x:xs) = ([], x, xs) : [(x:ys1, y, ys2) | (ys1, y, ys2) <- deconstruct xs]

What this function does is to obtain all the decompositions of a list xs into triples of form (ys1, y, ys2) such that ys1 ++ [y] ++ ys2 == xs. So for example:

deconstruct [1..4] => [([],1,[2,3,4]),([1],2,[3,4]),([1,2],3,[4]),([1,2,3],4,[])]

Using this you can define your function as follows:

checkNotSameScope xs n e =
    case [ys | (ys, ("$", Undefined), _) <- deconstruct xs] of
        [ys] -> BoolLit $ not $ alreadyThere n xs
        _    -> -- handle the case when ("$", Undefined) doesn't occur at all or more than once

We can use the do-notation to obtain something even closer to what you are looking for:

checkNotSameScope xs n e = BoolLit $ not $ any (alreadyThere n) prefixes
    where
        prefixes = do
            (xs, ("$", Undefined), _) <- deconstruct xs
            return xs

There are several things going on here. First of all the prefixes variable will store all the prefix lists which occur before the ("$", Undefined) pair - or none if the pair is not in the input list xs. Then using the any function we are checking whether alreadyThere n gives us True for any of the prefixes. And the rest is to complete your function's logic.



来源:https://stackoverflow.com/questions/12884674/haskell-pattern-matching-conundrum

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!