【问题标题】:counting a data type non-recursively非递归地计算数据类型
【发布时间】:2014-11-08 21:12:42
【问题描述】:

我有一个数据类型:

datatype int A = leaf of int * string
               | trunk of int * (int A) list

如果我有 test: int A,那么我想计算 test 中的叶子和树干,并将它们作为一对返回:(#leafs,#trunks)。但是我不想使用递归。

这是我的尝试:

  fun count(test: int A): int*int =
      case test
         of trunk(a,l) =>
            let
               val l1 = List.filter(fn trunk(a',l') => true
                                     | _ => false) l
               val l2 = List.filter(fn leaf(a',s) => true
                                     | _ => false) l
            in
               (List.length(l2), List.length(l1) + 1)
            end
          | leaf(a,s) => (1, 0)

只要 l' 为 nil,它就可以工作。如您所见,我将发束和树干分成不同的列表并返回长度。 但是,l' 可以包含其他未计算在内的叶子和树干。请注意,这不是递归的。我一直在考虑尝试使用延续,但我不知道该怎么做。 请问有什么建议吗?

【问题讨论】:

  • 你能帮我知道为什么你不想在这里使用递归吗?这是一种非常自然的推理方式。
  • 我更喜欢使用reduce和map之类的列表操作!这是一种不同的做事方式。
  • 让我们考虑一个更简单的数据类型 (datatype 'a tree = trunk of 'a tree list) 和一个示例。您如何找到此中继线的中继线数量val x = trunk( [trunk( [trunk( [] )] )] )
  • 鉴于您的数据结构的递归性质,您很难避免递归。您可以通过为您的树编写 map 和 reduce 函数来避免显式递归。
  • @JamesAndrews 完成!!

标签: recursion sml smlnj


【解决方案1】:

由于我们的数据类型与列表不同,我们需要为它创建一个显式的 reduce 函数。这个reduce函数接受一个应用于每个叶子的函数(ff)。它还接受一个函数(fd),该函数应用于每个主干并递归组合结果。它看起来像这样:

  fun reduce(ff:('a*string -> 'b))(fd:('a * 'b list -> 'b))(s: int A): 'b =
      case s
         of leaf(a, d) => ff(a,d)
          | trunk(a, l)  => fd(a, List.map(fn x => reduce(ff)(fd)(x)) l)

最后我们的计数函数会是这样的:

fun count(s: int A): int*int =

  let
     val i = reduce(fn x => 1)(fn (y,l) => foldr op+ 0 l)(s)
     val i' = reduce(fn x => 0)(fn (y,l) => 1 + foldr op+ 0 l)(s)
  in
     (i, i')
  end

也就是说,在 (i) 中,如果找到叶子,则将其替换为 1,如果找到树干,则将其替换为 0,并对整个列表求和。 在 (i') 中,如果找到叶子,则将其替换为 0。否则,如果找到树干,则添加 1 并求和!

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2023-03-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-09-03
    • 2013-08-06
    相关资源
    最近更新 更多