haskell - Haskell向构造函数添加约束
问题描述
背景:
我正在学习关于“语言和自动机”的课程:目前是关于正则表达式/语言、DFA 和 NFA。这个问题不是作业,而是在 Haskell 中实现我学到的一些东西,我决定为自己做。回答这个问题只需要 Haskell 的知识
我有以下用于正则表达式的数据类型
data Regex sigma = Eps
| Phi
| S sigma
| Regex sigma :. Regex sigma
| Regex sigma :| Regex sigma
deriving (Show,Eq,Read)
有了这个,我们可以定义正则表达式,例如S 'a'
, or S 'a' :. S 'b'
, or Eps :| S 'a'
。到目前为止,一切都很好。
现在,Eps
(空字符串)应该是 的中性元素:.
。即Eps :. e
并且e :. Eps
都应该返回e
(e
任何其他正则表达式在哪里)。
所以我想将模式匹配应用于构造函数(:.)
。这可能吗?如果是这样,我该如何实施?如果不是,那么实现我想要的另一种方法是什么?
我试图明确定义构造函数以应用模式匹配:
(:.) :: Regex sigma -> Regex sigma -> Regex sigma
(:.) Eps e = e
(:.) e Eps = e
(:.) e f = e :. f --this line is obviously incorrect, but I don't know how to write it differently
解决方案
听起来您可能想使用智能构造函数。基本思想是数据类型的实际构造函数是隐藏的,并且您可以公开这些构造函数的特殊版本。这在构造上非常有用,但正如@bradrn 在评论中暗示的那样,它可能会让可能正在解构您的数据类型的用户感到困惑。
在实践中,使用智能构造函数会是这样的:
module Regex (Regex, eps, phi, s, (.:), (.|)) where
data Regex sigma = Eps
| Phi
| S sigma
| Regex sigma :. Regex sigma
| Regex sigma :| Regex sigma
deriving (Show,Eq,Read)
eps :: Regex sigma
eps = Eps
s :: sigma -> Regex sigma
s = S
...
(.:) :: Regex sigma -> Regex sigma -> Regex sigma
(.:) Eps e = e
(.:) e Eps = e
(.:) e f = e :. f
首先,请注意实际的构造函数Regex
没有被导出。另请注意,如果Regex
使用智能构造函数创建 a,那么您正在寻找的简化:.
会自动发生。您可以根据需要在这些智能构造函数中添加尽可能多的简化。最后,请注意,Eps .: e == e .: Eps
就像您想要的那样,您不必重新定义Eq Regex
实例。
当然,这样做的缺点是用户无法访问实际的Regex
构造函数,这意味着用户无法解构Regex
. 有一些方法可以解决这个问题,例如使用单向或双向模式,但这可能会令人困惑。例如(正如@bradrn 在评论中指出的那样),该值Eps .: e
与 pattern 不匹配x :. y
。
还有一个问题是您的派生Read
实例仍可用于创建Regex
具有类似内容Eps :. e
的值。一个巧妙的提示是,现在您有了智能构造函数,定义变得非常容易simplifyRegex :: Regex σ -> Regex σ
:
simplifyRegex :: Regex sigma -> Regex sigma
simplifyRegex Eps = eps
simplifyRegex Phi = phi
simplifyRegex (S x) = s x
simplifyRegex (x :. y) = simplifyRegex x .: simplifyRegex y
simplifyRegex (x :| y) = simplifyRegex x |: simplifyRegex y
有了这个,很容易制作一个新版本read
:
readRegex :: Read sigma => String -> Regex sigma
readRegex = simplifyRegex . read
最后一个有趣的注释: 的定义simplifyRegex
遵循一种看起来很像折叠的标准形式。事实上,我们甚至可以将其抽象一点,并将其转化为本质上是折叠的东西:
foldRegex :: a -> a -> (sigma -> a) -> (a -> a -> a) -> (a -> a -> a) -> Regex sigma -> a
foldRegex eps phi s (.:) (|:) = go
where
go Eps = eps
go Phi = phi
go (S x) = s x
go (x :. y) = go x .: go y
go (x :| y) = go x |: go y
此函数将要为每个可能的Regex
构造函数做的事情作为参数,然后递归地解构Regex
,在每个点应用事情。我们可以通过将其定义为轻松恢复该simplifyRegex
功能
simplifyRegex :: Regex sigma -> Regex sigma
simplifyRegex = foldRegex eps phi s (.:) (|:)
我们调用foldRegex
所有智能构造函数的地方。
通过将此函数发布给您的用户,您可以让他们解构任何内容Regex
,而无需实际授予他们对构造函数的直接访问权限Regex
。
要了解更多信息,请搜索“catamorphisms”和“recursion scheme”。
推荐阅读
- redis - .Net Core Redis 中的 IDistributedCache 和 IConnectionMultiplexer 接口有什么区别?
- firebase - Flutter + Firebase - 将电话号码与现有帐户关联
- python - 当你有足够的数据时如何解决过拟合
- drupal - 用于获取针对 URL 的内容类型的 API
- ruby-on-rails - Graphql-ruby:错误“字段必须有选择”
- loops - 迭代截断的 md5 哈希的挑战
- linux - 在SVN中恢复冲突解决
- linux - 仅当主机名的前 3 个字母等于 ansible 中的特定单词时,如何启动任务?
- csv - 在 Elasticsearch 中导入 CSV 文件
- pivot-table - 基于数据透视行标签分组的数据透视度量