在 Purescript 中使用 Data.String.Regex(正则表达式)

Using Data.String.Regex (regex) in Purescript

学习纯文字。我尝试在 Data.String.Regex (test) 的文件名列表中使用 Data.Foldable (find)test 需要 Regex 作为输入,但 Data.String.Regex (regex) 返回(或创建?)结果(或值?或函数?)Either String Regex.

问题是:这两天我找不到任何在纯脚本中处理 Either 以及如何将其合并到复杂表达式中的示例(无法理解如何混合和匹配多个函数及其输出结果一起)。

我想做这样的事情: searched_item = find test regex """^.*$""" noFlags ns 但是我所有的实验都编译失败,导致错误。

我试图寻找 Haskel/Elm 示例,但据我了解他们的 regex 实现 return Regex(不是 Either String Regex)并且可以使用马上。

我知道如果 regex 无法将字符串解析为正则表达式,则 Either 用于错误处理,我需要以某种方式使用 LeftRight .我只是无法从文档中获取规则或找不到易于理解的初学者级别的示例。

编辑: 实际编译的 Regex 的一项实验是:

re :: Either String Regex
re = regex """^\d+_sh_\d+_\d+[a-z]\.csv$""" noFlags
str :: String
str = "1_sh_2_3a.csv"
f :: Either String Regex -> String -> Boolean
f r s = case r of
  Left _ -> false
  Right r' -> test r' s
res :: Boolean
res = f re str

并且 restrue。 (顺便说一句!有没有办法以更少的行将它们粘合在一起?)

但我如何将它粘贴到类似的东西中:

main = do
  ns <- readdir "."
  for ns log
  -- TODO: find and log a fliename

一个完全可行的替代方案是搞砸安全:如果你的正则表达式在编译时是已知的(即不是来自用户或数据库或其他类似的),并且你确定它是正确的,你可以 "swear" 告诉编译器结果永远不会是 Left。为此,请使用标准函数 fromRight, which is partial (i.e. sometimes crashes), so you'll need to wrap it in unsafePartial 来防止编译器警告:

re :: Regex
re = unsafePartial $ fromRight $ regex """^\d+_sh_\d+_\d+[a-z]\.csv$""" noFlags

如果正则表达式格式不正确,这将崩溃,所以要小心。


如果你不想那样放弃安全,那么你所做的或多或少就是这样,当然你可以考虑得更好一些。

首先要知道的是运算符<$><*><@>,通常统称为"splat"。他们的生活目的是获取一个函数并将其应用于某个容器的"inside"。他们使用任何具有 "value inside" 的东西 - Either 就是其中之一。

因此,例如,这有效:

a :: Either String Int
a = Right 40

plus2 :: Int -> Int
plus2 x = x + 2

b :: Either String Int
b = plus2 <$> a

-- Now b == Right 42

(为了以后参考,有"value inside"的东西一般被称为"functor")

您也可以对具有多个参数的函数执行此操作,例如(为简洁起见省略类型签名):

a = Right 40
b = Right 2
plus x y = x + y
c = plus <$> a <*> b
-- Now c == Right 42

请注意,为了使用运算符 <*>,第二个参数也必须是 "inside a functor"。如果您的第二个参数恰好是独立的,没有仿函数,请使用运算符 <@>:

a = Right 40
b = 2
c = plus <$> a <@> b

现在,有了这些知识,我们可以像这样实现您的测试功能:

f r s = test <$> r <@> s

但是,当然,现在函数 f returns 和 Either String Boolean - 即结果 Boolean 仍然包裹在 Either.

因此,为了从 Either 中获取该值,您需要一个函数 Either String Boolean -> Boolean,或者,更一般地说,您可以查看 b -> Either a b -> b 这样的函数,其中"if the value is Right, return it, otherwise return the provided fallback value" 的语义。它将像这样实现:

ifLeft :: forall a b. b -> Either a b -> b
ifLeft _ (Right b) = b
ifLeft b (Left _) = b

出于某种我不太明白的原因,这样的函数在任何 "standard" PureScript 库中都不存在(它确实存在于 Haskell 中,但 - fromRight). So unfortunately you'll have to include your own implementation, or you can opt for using some of the existing combinators,例如:

-- Alternative option 1
ifLeft b = either (const b) identity

-- Alternative option 2
ifLeft b = fromMaybe <<< hush b

将以上所有内容结合在一起,您的程序将如下所示:

re :: Either String Regex
re = regex """^\d+_sh_\d+_\d+[a-z]\.csv$""" noFlags

ifLeft :: forall a b. b -> Either a b -> b
ifLeft _ (Right b) = b
ifLeft b (Left _) = b

res :: Boolean
res = ifLeft false $ test <$> re <@> "1_sh_2_3a.csv"

当然,这并没有缩短多少,但现在您可以稍后重用 ifLeft 函数,以实现不同的目的。或者,您也可以在 Either 包装器中执行一些更复杂的计算,并且只在最后打开它。

或者,或者,将 ifLeft 的显式定义替换为对标准组合器 either:

的调用
re :: Either String Regex
re = regex """^\d+_sh_\d+_\d+[a-z]\.csv$""" noFlags

res :: Boolean
res = either (const false) identity $ test <$> re <@> "1_sh_2_3a.csv"