Algorithm 用线性供应流中的值填充嵌套结构

Algorithm 用线性供应流中的值填充嵌套结构,algorithm,data-structures,functional-programming,stream,nested,continuation-passing,Algorithm,Data Structures,Functional Programming,Stream,Nested,Continuation Passing,我陷入了下一个问题的解决: 假设我们有一个数组结构,任何结构,但在本例中,让我们使用: [ [ [1, 2], [3, 4], [5, 6] ], [ 7, 8, 9, 10 ] ] 为了方便起见,我将此结构转换为平面阵列,如: [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ] 设想在执行某些操作后,我们的阵列如下所示: [ 1, 2, 3, 4, 12515, 25125, 12512, 8, 9, 10] 注意:这些值是一些操作的结果,我只想指出,它们

我陷入了下一个问题的解决:

假设我们有一个数组结构,任何结构,但在本例中,让我们使用:

[
    [ [1, 2], [3, 4], [5, 6] ],
    [ 7, 8, 9, 10 ]
]
为了方便起见,我将此结构转换为平面阵列,如:

[ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ]
设想在执行某些操作后,我们的阵列如下所示:

[ 1, 2, 3, 4, 12515, 25125, 12512, 8, 9, 10]
注意:这些值是一些操作的结果,我只想指出,它们与结构或它们的位置无关

我想知道的是。。。给定第一个数组结构,如何将最后一个平面数组转换为与第一个相同的结构?所以它看起来像:

[ 
   [ [1, 2], [3, 4] , [12515, 25125] ],
   [ 12512, 8, 9, 10] 
]

有什么建议吗?我只是将这些位置硬编码到给定的结构中。但这不是动态的

只需在结构中递归,然后使用迭代器按顺序生成值:

函数fillWithStream(结构、迭代器){

对于(var i=0;i这是Scala中的一个草图。无论您的语言是什么,您首先必须以某种方式表示树状数据结构:

sealed trait NestedArray
case class Leaf(arr: Array[Int]) extends NestedArray {
  override def toString = arr.mkString("[", ",", "]")
}
case class Node(children: Array[NestedArray]) extends NestedArray {
  override def toString = 
    children
      .flatMap(_.toString.split("\n"))
      .map("  " + _)
      .mkString("[\n", "\n", "\n]")
}

object NestedArray {
  def apply(ints: Int*) = Leaf(ints.toArray)
  def apply(cs: NestedArray*) = Node(cs.toArray)
}
唯一重要的部分是区分包含整数数组的叶节点和包含其子节点的内部节点。
toString
方法和额外的构造函数并不重要,主要是为了下面的小演示

现在您需要构建一个编码器-解码器,
encode
部分简单地将所有内容展平,而
decode
部分将另一个嵌套数组作为参数,并将一个扁平数组重塑为嵌套数组的形状。展平非常简单:

def encode(a: NestedArray): Array[Int] = a match {
  case Leaf(arr) => arr
  case Node(cs) => cs flatMap encode
}
恢复结构也不是那么困难。我决定通过传递显式的
int
-索引来跟踪数组中的位置:

def decode(
  shape: NestedArray, 
  flatArr: Array[Int]
): NestedArray = {
  def recHelper(
    startIdx: Int, 
    subshape: NestedArray
  ): (Int, NestedArray) = subshape match {
    case Leaf(a) => {
      val n = a.size
      val subArray = Array.ofDim[Int](n)
      System.arraycopy(flatArr, startIdx, subArray, 0, n)
      (startIdx + n, Leaf(subArray))
    }
    case Node(cs) => {
      var idx = startIdx
      val childNodes = for (c <- cs) yield {
        val (i, a) = recHelper(idx, c)
        idx = i
        a
      }
      (idx, Node(childNodes))
    }
  }
  recHelper(0, shape)._2
}
以下是ASCII树的外观:

[
  [
    [1,2]
    [3,4]
    [5,6]
  ]
  [
    [7,8,9,10]
  ]
]
现在,从不同阵列重建相同形状的树:

val flatArr = Array(1, 2, 3, 4, 12515, 25125, 12512, 8, 9, 10)
val reconstructed = decode(original, flatArr)

println(reconstructed)
这将为您提供:

[
  [
    [1,2]
    [3,4]
    [12515,25125]
  ]
  [
    [12512,8,9,10]
  ]
]
我希望,对于那些在不太遥远的ML后代中进行函数式编程的人来说,这应该或多或少是可以理解的。

几个月前,你的问题就被证明了,无论如何,这是一个非常类似的问题

那里的代码需要稍微调整一下,使其适合这里。在Scheme中:

(定义(合并树边缘VAL树k)
(续)
[(空?树)
(k vals'())]
[(不是(成对树));对于每片叶子:
(k(cdr VAL)(car VAL))];使用第一个VAL
[其他
(合并树边缘VAL(汽车树)(lambda(汽车r);从汽车收集“r”,
(合并树边缘Avals(cdr树)(lambda(Dvals q);从cdr收集“q”,
(k Dvals(cons r q()()()())))]);返回最后的VAL和组合结果
第一个参数是值的线性列表,第二个参数是要重新创建其结构的嵌套列表。请确保线性值列表中有足够的元素

我们称之为

> (merge-tree-fringe '(1 2 3 4 5 6 7 8) '(a ((b) c) d) (lambda (vs r) (list r vs)))
'((1 ((2) 3) 4) (5 6 7 8))

> (merge-tree-fringe '(1 2 3 4 5 6 7 8) '(a ((b) c) d) (lambda (vs r) r))
'(1 ((2) 3) 4)
在链接的答案中有一些措辞,解释了正在发生的事情。短篇小说,用–风格写成:

我们处理嵌套结构的一部分,同时用线性供应的值替换叶子;然后我们用剩余供应处理结构的其余部分;然后我们将处理这两个子部分得到的两个结果合并起来。对于类似LISP的嵌套列表,通常是“
car
”和“
cons
”单元格的“
cdr
”,即树的顶部节点

这就是Bergi的代码所做的,本质上是以功能的方式


在一个假想的模式匹配伪代码中,它可能更容易阅读/遵循,但实际上是

合并树边缘VAL树=g VAL树(vs r=>r)
哪里
g VAL[a,…d]k=g VAL a(avals r=>--avals:a之后剩余的VAL
g avals d(dvals q=>--dvals:在“d”之后剩余
k dvals[r,…q])——合并结果
g VAL[]k=k VAL[]为空
g[v,…vs]\uk=k vs v--leaf:替换它
这种在计算中穿行变化状态的计算模式正是状态单子的作用所在;使用Haskell的
do
表示法,上述内容可以写成

merge_tree_marge vals tree=evalState(g tree)vals
哪里

g[a,…d]=do{r您的结构是否仅限于数组和子数组?@Yevgeniy.Chernobrivets是的,只有数组和数组的数组,没有对象,没有其他类型的值…如果您想对其进行更多约束。数组的int显示关于在数组“坐标”中保留每个数字“在初始结构中,如下所示:第一个坐标位于第一级数组中,第二个坐标位于第二级数组中,等等。您可以不指定所有坐标,例如仅指定第一个坐标,这意味着数字位于第一个数组中,而不在子数组中。这将创建大量我已经知道的附加数据(通过使用第一个结构,我知道数组中的第一个元素映射到子数组的子数组中的第一个位置)因此,我认为在这种情况下,向每个数字添加坐标不是一个好的解决方案。这将增加我所能说的最大的灵活性。我将从它开始,并继续消除冗余信息。我认为,为了重建初始结构,您将需要存储一些额外的信息。谢谢Bergi,我喜欢您的方法,但事实并非如此与所选答案相比,解释有点糟糕。但我喜欢你使用迭代器和递归的方法。这非常好。我添加了一个基于CPS的答案,顺便说一句;有趣的是,似乎不同的方法实际上描述了非常相同的事情。@Willenss我发现更有趣的是,这个问题在三个小时内得到了回答不同的语言:-)我通常
> (merge-tree-fringe '(1 2 3 4 5 6 7 8) '(a ((b) c) d) (lambda (vs r) (list r vs)))
'((1 ((2) 3) 4) (5 6 7 8))

> (merge-tree-fringe '(1 2 3 4 5 6 7 8) '(a ((b) c) d) (lambda (vs r) r))
'(1 ((2) 3) 4)