如何才能根据列表的后半部分简洁地匹配列表,并相应地绑定它的前半部分?

时间:2019-05-17 17:51:53

标签: haskell

假设我有一个函数f :: String -> String,并且想匹配以下形式的参数

_ ++ "bar"

其中_是我想返回的未指定字符串。换句话说,我要匹配foobarbazbar之类的参数,并分别返回foobaz

可以使用ViewPatterns,如下所示:

{-# LANGUAGE ViewPatterns #-}

f :: String -> String
f x@(reverse . take 3 $ reverse -> "bar") = take (n-3) x
    where n = length x

...但这远非理想。主要是因为如果我决定要组合两个或多个这样的模式,事情将很快变得毛茸茸。

理想情况下,我希望某些东西能够写这样的东西:

f (x:"bar") = x

但不幸的是,这不是有效的Haskell。

ViewPatterns或其他扩展中是否有适当的解决方案?

4 个答案:

答案 0 :(得分:3)

在内置String上,这是一个非常糟糕的主意,因为模式匹配非常昂贵。在其他字符串类型上,例如TextByteString,您可以使用模式防护:

{-# LANGUAGE OverloadedStrings #-}

import qualified Data.Text as T

f :: Text -> Text
f x | Just x' <- T.stripSuffix "bar" x = ...

或使用ViewPatterns(我不太喜欢):

{-# LANGUAGE OverloadedStrings, ViewPatterns #-}

import qualified Data.Text as T

f :: Text -> Text
f (T.stripSuffix "bar" -> Just x') = ...

答案 1 :(得分:2)

stripSuffix :: (Eq a) => [a] -> [a] -> Maybe [a]
stripSuffix needle = go <*> drop (length needle)
  where
    go xs [] = if xs == needle then Just [] else Nothing
    go (x:xs) (_:ys) = (x:) <$> go xs ys

f (stripSuffix "bar" -> Just pref) = pref

我还没有对其进行过多的测试,但这是一个简单的解决方案,不会带来额外的正则表达式/解析器机制。

答案 2 :(得分:0)

请勿使用模式匹配。模式匹配通常很便宜,并且可以匹配输入数据的结构。这是一个非常昂贵的模式,好像计算起来非常便宜。如果要执行此操作,请将其写为保护子句,在其中可以清楚地说明正在发生的事情。

答案 3 :(得分:0)

使用TemplateHaskell,这几乎是 -对此有经验的人可以改善此答案。

import Language.Haskell.TH
import Language.Haskell.TH.Syntax

(+++) :: Q Pat -> String -> Q Pat
x +++ y = [p| ((\string -> splitAt (length string - length y) string) -> (x, $literal_pattern)) |]
  where literal_pattern = returnQ (LitP (StringL y))

这在模式位置可用,您可以使用TemplateHaskell的准引用将模式作为第一个参数传递:

f $([p|x|] +++ "bar") = x

令人烦恼的是,我找不到任何解释能比这更简洁地将模式传递给TemplateHaskell接头。