【问题标题】:How to implement distributive property in Haskell recursive data type?如何在 Haskell 递归数据类型中实现分布式属性?
【发布时间】:2020-05-22 23:41:16
【问题描述】:

我的任务是更正一个函数expand

infixl 6 :+:
infixl 7 :*:
data Expr = Val Int | Expr :+: Expr | Expr :*: Expr
    deriving (Show, Eq)

expand :: Expr -> Expr
expand ((e1 :+: e2) :*: e) = expand e1 :*: expand e :+: expand e2 :*: expand e
expand (e :*: (e1 :+: e2)) = expand e :*: expand e1 :+: expand e :*: expand e2
expand (e1 :+: e2) = expand e1 :+: expand e2
expand (e1 :*: e2) = expand e1 :*: expand e2
expand e = e

-- expression example: (Val 1 :+: Val 2 :+: Val 3) :*: (Val 4 :+: Val 5)
-- which is equivalent to (1 + 2 + 3) * (4 + 5)

-- expression, that given fucntion evaluates our example to: 
--(Val 1 :+: Val 2) :*: (Val 4 :+: Val 5) :+: Val 3 :*: (Val 4 :+: Val 5)

-- expression that corrected function must evaluate our example to:
-- Val 1 :*: Val 4 :+: (Val 1 :*: Val 5 :+: (Val 2 :*: Val 4 :+: (Val 2 :*: Val 5 :+: (Val 3 :*: Val 4 :+: Val 3 :*: Val 5))))

-- answers like (Val 1 :*: Val 2) :+: (Val 3 :*: Val 4) 
-- and          (Val 4 :*: Val 3) :+: (Val 1 :*: Val 2)
-- are considered to be equal

它不能正常工作,因为它只打开一次括号。因此,我将其修改为:

infixl 6 :+:
infixl 7 :*:
data Expr = Val Int | Expr :+: Expr | Expr :*: Expr
    deriving (Show, Eq)

expand :: Expr -> Expr
expand ((e1 :+: e2) :*: e) = (expand $ e :*: e1) :+: (expand $ e :*: e2)
expand (e :*: (e1 :+: e2)) = (expand $ e :*: e1) :+: (expand $ e :*: e2)
expand (e1 :+: e2) = expand e1 :+: expand e2
expand expr@(e1 :*: e2) = if isMul expr
                          then expr
                          else expand $ expand e1 :*: expand e2
expand e = e

isMul :: Expr -> Bool
isMul (Val a :*: expr) = isMul expr 
isMul (expr :*: Val a) = isMul expr
isMul (Val a) = True
isMul  _      = False

添加函数isMul 以查找边缘条件:如果我们的表达式(e1 :*: e2)Val 1 :*: Val 2 :*: Val 3 ... 的形式,那么我们的函数expand 将停止扩展并将表达式计算为自身,否则递归继续。

在我的例子中效果很好~ish

exp0 = (Val 1 :+: Val 2 :+: Val 3) :*: (Val 4 :+: Val 5)
exp1 = (Val 1) :*: ((Val 2) :+: (Val 3)) :*: (Val 4)
exp2 =  Val 1 :*: (Val 2 :*: (Val 3 :+: Val 4)) 
exp3 = ((Val 1) :+: (Val 2)) :*: ((Val 3) :+: (Val 4))
exp4 =  Val 2 :*: (Val 3 :+: Val 4)
exp5 = (Val 3 :+: Val 4) :*: Val 2
exp6 =  Val 3 :+: Val 4  :*: Val 2
exp7 =  Val 3 :*: (Val 4 :*: Val 2)
exp8 = (Val 3 :*: Val 4) :*: Val 2
exp9 =  (Val 1 :+: Val 2 :+: Val 3) :*: (Val 4 :+: Val 5) :*: (Val 6) :*: ((Val 7) :+: (Val 8)) :*: (Val 9)

由于超出时间限制,但未能通过某些测试。我猜,递归不会停在某个地方,它必须停在哪里,但我不知道在哪里。

【问题讨论】:

  • “未能通过测试”?那将是一个快速检查,还是什么?你能提取出没有完成的具体测试用例吗?
  • 我不知道这些测试是什么,也不是提取它们的方法。我对它们的了解是: 1. 时间限制:5 秒,内存限制:256 MB 2. 通过标准输入测试 → 标准输出 3. 失败。超过时间限制

标签: haskell algebra


【解决方案1】:

函数isMul 有错误:表达式isMul ((Val 3 :*: (Val 4 :*: Val 2)) :*: ((Val 3 :*: Val 4) :*: Val 2)) 的结果是False。发生了,因为data Expr本质上是一棵树,所以表达式Val 1 :*: (Val 2 :*: (Val 3 :*: Val 4))(Val 1 :*: Val 2) :*: (Val 3 :*: Val 4)其实是不同的。所以,我为isMul写了一个更一般的实现:

isMul :: Expr -> Bool
isMul (e1 :*: e2) = isMul e1 && isMul e2
isMul (Val a) = True
isMul  _      = False

这使程序按预期工作。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2014-06-24
    • 2022-01-06
    • 2016-09-20
    • 2019-04-30
    • 1970-01-01
    • 2014-08-13
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多