Tree 使用reduce和map遍历n元树的SML

Tree 使用reduce和map遍历n元树的SML,tree,sml,Tree,Sml,我是SML的新手 假设我有以下数据类型: datatype 'a tree = leaf of 'a | node of 'a tree list 函数val leaves=fn:'a tree->'是一个列表: fun leaves (leaf x) = [x] | leaves (node []) = [] | leaves (node [x]) = leaves x | leaves (node (x::xs)) = (leaves x) @ (leaves (node xs

我是SML的新手

假设我有以下数据类型:

datatype 'a tree = leaf of 'a | node of 'a tree list
函数
val leaves=fn:'a tree->'是一个列表

fun leaves (leaf x) = [x]
  | leaves (node []) = []
  | leaves (node [x]) = leaves x
  | leaves (node (x::xs)) = (leaves x) @ (leaves (node xs))
如果我有

val t = node [node [leaf 1,
              node [leaf 2, leaf 3],
              leaf 4]];
然后,
leaves t
将为我返回
[1,2,3,4]

我想问的是,这是否可以通过使用
reduce
map
重写

鉴于:

fun reduce g [x] = x
  | reduce g (x::xs) = (g x (reduce g xs))

非常感谢。

您似乎有一种反复出现的习惯,就是从太多的子句开始

从两个必要的方面开始:

fun leaves (leaf x) = [x]
  | leaves (node xs) = ???
现在,
xs
是一个列表。
map
将列表转换为不同的列表。
reduce
将列表缩减为单个值

这表明你想要某种形式的东西

| leaves (node xs) = reduce f (map g xs)

确定
f
g
作为练习留在左侧。

在ML中,有一种约定,在SML中没有语法强制,即数据构造函数以大写开头,值(包括函数)以小写开头。在模式匹配中,这使它们更容易区分,因为在语法上,
节点可以是数据构造函数或变量,如果不检查整个代码,就无法分辨

StackOverflow倾向于同意我们的观点,因为它的语法highlighter不是非常上下文感知的,除非我们遵循这个约定,否则无法为数据构造函数绘制特殊的颜色

因此:


molbdnilo已经展示了如何简化
leaves
中的模式

fun leaves (Leaf x) = [x]
  | leaves (Node ts) = ???
(注意,我更改了构造函数的大小写,并将
xs
重命名为
ts
,因为它是一个树列表。)

为了使用递归来完成这个函数,我想说这里有两种方法。正如您将看到的,在两个方向上都有权衡,我将提到:

  • 坚持使用两种模式,并在一个单独的、相互递归的函数中处理
    xs
    上的递归:

    fun leaves (Leaf x) = [x]
      | leaves (Node ts) = nodes ts
    
    and nodes [] = []
      | nodes (t::ts) = leaves n @ nodes ts
    
    拥有两个可能互相调用的函数只是有点让人费解。您必须不断提醒自己,
    t
    是一棵树,因此应该对其调用
    leaves
    ,但
    ts
    是一个树列表,因此应该对其调用
    节点。但是除了在后续的、相互递归的函数上使用
    而不是
    fun
    之外,语法也没那么糟糕

  • 转到三种模式,通过匹配两个深度级别来处理树及其内部列表的递归:

    fun leaves (Leaf x) = [x]
      | leaves (Node []) = []
      | leaves (Node (t::ts)) = leaves t @ leaves (Node ts)
    
    只有一个函数似乎更简单,但缺点是模式匹配变得有点棘手:您不仅要匹配输入是一个
    节点
    ,还要特别匹配它是一个
    节点
    ,没有子节点(
    节点[]
    ),或者至少有一个子节点(
    节点(t::ts)
    )。这里的复杂性在于,虽然模式匹配成功地解构了单个
    t
    ,可以递归地传递给
    leaves
    leaves t
    ),处理其余的
    ts
    (一个
    “树列表”
    ),但您没有处理此类列表的函数。但您几乎可以这样做:如果您将
    节点
    放回
    ts
    Node ts
    ),您将得到一个包含该子节点列表的
    'a树
    。你有一个处理一棵树的函数

随着时间的推移,你可以自己决定哪种想法更难处理

也许在不同的场合你都喜欢


我想问的是,这是否可以通过使用
reduce
map
重写


是的。

在研究了
List.concat
之后,我知道
f
g
是什么样子!谢谢你的评论!我以前没有注意到命名约定。是的,我同意你的观点,我们应该坚持下去:)
fun leaves (Leaf x) = [x]
  | leaves (Node []) = []
  | leaves (Node (t::ts)) = leaves t @ leaves (Node ts)