gpt4 book ai didi

haskell - 在模式保护或 case 表达式中重用模式

转载 作者:行者123 更新时间:2023-12-02 21:05:01 25 4
gpt4 key购买 nike

我的 Haskell 项目包括一个表达式计算器,出于这个问题的目的,它可以简化为:

data Expression a where
I :: Int -> Expression Int
B :: Bool -> Expression Bool
Add :: Expression Int -> Expression Int -> Expression Int
Mul :: Expression Int -> Expression Int -> Expression Int
Eq :: Expression Int -> Expression Int -> Expression Bool
And :: Expression Bool -> Expression Bool -> Expression Bool
Or :: Expression Bool -> Expression Bool -> Expression Bool
If :: Expression Bool -> Expression a -> Expression a -> Expression a

-- Reduces an Expression down to the simplest representation.
reduce :: Expression a -> Expression a
-- ... implementation ...

实现此目的的直接方法是编写一个 case 表达式来递归求值和模式匹配,如下所示:

reduce (Add x y) = case (reduce x, reduce y) of
(I x', I y') -> I $ x' + y'
(x', y') -> Add x' y'
reduce (Mul x y) = case (reduce x, reduce y) of
(I x', I y') -> I $ x' * y'
(x', y') -> Mul x' y'
reduce (And x y) = case (reduce x, reduce y) of
(B x', B y') -> B $ x' && y'
(x', y') -> And x' y'
-- ... and similarly for other cases.

对我来说,这个定义看起来有点尴尬,所以我然后使用模式防护重写了定义,如下所示:

reduce (Add x y) | I x' <- reduce x
, I y' <- reduce y
= I $ x' + y'

我认为与 case 表达式相比,这个定义看起来更清晰,但是当为不同的构造函数定义多个模式时,该模式会重复多次。

reduce (Add x y) | I x' <- reduce x
, I y' <- reduce y
= I $ x' + y'
reduce (Mul x y) | I x' <- reduce x
, I y' <- reduce y
= I $ x' * y'

注意到这些重复的模式,我希望有一些语法或结构可以减少模式匹配中的重复。是否有一个普遍接受的方法来简化这些定义?

编辑:在检查了模式防护之后,我意识到它们不能作为直接替代品。虽然当xy可以减少到I _时它们提供相同的结果,但当模式防护不减少任何值时,它们不会减少任何值。匹配。我仍然希望使用 reduce 来简化 Add 等的子表达式。

最佳答案

我在类似情况下使用过的一个部分解决方案是将逻辑提取到一个“提升”函数中,该函数采用正常的 Haskell 操作并将其应用于您的语言的值。这对包装/展开以及由此产生的错误处理进行了抽象。

这个想法是创建两个类型类,用于传入和传出您的自定义类型,并进行适当的错误处理。然后您可以使用它们创建一个 liftOp 函数,如下所示:

liftOp :: (Extract a, Extract b, Pack c) => (a -> b -> c) -> 
(Expression a -> Expression b -> Expression c)
liftOp err op a b = case res of
Nothing -> err a' b'
Just res -> pack res
where res = do a' <- extract $ reduce' a
b' <- extract $ reduce' b
return $ a' `op` b'

然后每个具体案例如下所示:

Mul x y -> liftOp Mul (*) x y

这还不算太糟糕:它并不太多余。它包含重要的信息:Mul 映射到 *,在错误情况下,我们只需再次应用 Mul

您还需要用于打包和解包的实例,但无论如何这些都是有用的。一个巧妙的技巧是,这些还可以让您使用 (Extract a, Pack b) => Pack (a -> b) 形式的实例自动将函数嵌入到 DSL 中。

我不确定这是否完全适合您的示例,但我希望它能为您提供一个良好的起点。您可能希望在整个过程中连接额外的错误处理,但好消息是大部分内容都被折叠到 packunpackliftOp 的定义中,所以它仍然相当集中。

wrote up针对相关(但有些不同)问题的类似解决方案。这也是处理本地 Haskell 值和解释器之间来回切换的一种方法,但解释器的结构不同。不过,一些相同的想法仍然适用!

关于haskell - 在模式保护或 case 表达式中重用模式,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/25311781/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com