【问题标题】:Haskell EvaluationHaskell 评估
【发布时间】:2019-12-02 05:36:59
【问题描述】:

我有以下代码块:

data G = F G G | H Int deriving (Show, Eq)

example :: Int -> G -> G
example 0 (H i) = (H i)
example n (H i) = F (example (n-1) (H i)) (example (n-1) (H i))
example n (F i1 i2) = F (example n i1) (example n i2)

当我运行 example 0 (F (H 1) (H 2)) 时,它按预期返回 F (H 1) (H 2)

当我运行 example 1 (F (H 1) (H 2)) 时,它返回 F (F (H 1) (H 1)) (F (H 2) (H 2))

这不是我想要的。我要回F (F (H 1) (H 1)) (H 2)

我的意思是在第 6 行,example n (F i1 i2) = F (example n i1) (example n i2),我调用了递归函数两次。但是我希望(example n i1) 先评估并更新变量n,然后再评估(example n i2)

对于这个确切问题的任何帮助/解决方案将不胜感激。我已经尝试了几个小时,但成功率为零。

【问题讨论】:

  • 如何使用example 更新nnInt,但 example 返回 G 值。您不能将 G 值绑定到 Int 变量。
  • 有没有办法解决这个问题?甚至不使用一些辅助函数?
  • “我希望(例如 n i1)首先评估并更新变量 n。” n“之后”(example n i1) 应该使用什么值?
  • 虽然example n (F i1 i2) = F (example n i1) i2 似乎与您的测试用例相匹配。根本不清楚,你想要什么。您的问题缺少规范。
  • 写一个帮助器exampleH :: Int -> G -> (G, Int),将n作为输入,并在返回的对中返回“更新的”n作为输出。

标签: haskell functional-programming


【解决方案1】:

问题似乎是您想使用example 来“更新”n。但是,您不能直接这样做,因为example 返回一个G 值,而n 是一个Int

看来,您需要一个函数G -> Int。问题是没有明确正确的方法来编写该函数。事实上,有无数种可能的实现方式。这是一个示例:

evalZero :: G -> Int
evalZero = const 0

evalOne :: G -> Int
evalOne = const 1

evalLeft :: G -> Int
evalLeft (H i) = i
evalLeft (F g _) = evalLeft g

evalRight :: G -> Int
evalRight (H i) = i
evalRight (F _ g) = evalRight g

evalSum :: G -> Int
evalSum (H i) = i
evalSum (F g1 g2) = evalSum g1 + evalSum g2

evalProduct :: G -> Int
evalProduct (H i) = i
evalProduct (F g1 g2) = evalProduct g1 * evalProduct g2

evalPlusOneProduct :: G -> Int
evalPlusOneProduct (H i) = i + 1
evalPlusOneProduct (F g1 g2) = evalPlusOneProduct g1 * evalPlusOneProduct g2

-- etc.

正如evalPlusOneProduct 所示,您可以任意添加或减去常量Int,或者,就此而言,乘以一个常量,或者执行更复杂的任意计算。由于有无限多的整数,因此有无限多的实现(尽管从技术上讲,我们受到Int 范围的限制)。

一旦您弄清楚如何将G 转换为Int,您就可以使用该函数从n 和左边的G 值中计算出一个新值n1

【讨论】:

  • 首先,非常感谢您花时间帮助我。但是,我不太了解该解决方案,或者我如何使用它/它的变体来达到我想要的效果。
  • 我的函数式编程经验非常有限。然而,在其他语言中,可以为 n 传递相同的指针来实现我想要的解决方案。
  • @Haskell_Adventurer 当您调用example n i1 时,它会产生一个G 值。使用G -> Int 函数将G 评估为Int。生成的Int 就是你的n1。现在调用example n1 i2
  • 我想我现在明白了。因此,我们将example n i1 的输出传递给G -> Int 类型的某个函数,该函数生成Int,比如n1,然后我们将其用于example n1 i2。但这是否意味着必须递归 example n i 两次?两次是指我们计算example n i1时的一次,以及我们使用它计算Int时的一次。
  • @Haskell_Adventurer 否,但您可能需要 let 绑定。 learnyouahaskell.com/syntax-in-functions