【问题标题】:How to convert nested function call tree to state machine?如何将嵌套函数调用树转换为状态机?
【发布时间】:2021-05-27 02:47:36
【问题描述】:

假设你有这种系统:

const input = [0, [1, 2, [3, 4, [8, 9], 6, 7], 4], [5, 6, 7], 3, 4, [8, 9], 6, 7, [1], 9]
const output = parse(input)
console.log(output.join('-'))

function parse(input) {
  const output = []
  iterate(input, value => {
    check(() => isArray(value),
      () => { // yes array
        const children = parse(value)
        output.push(...children)
      },
      () => { // not array
        check(() => isEven(value), () => {
          output.push(value)
        })
      })
  })
  return output
}

function isArray(value) {
  return Array.isArray(value)
}

function isEven(value) {
  return value % 2 == 0
}

function check(condition, success, failure) {
  if (condition()) success()
  else if (failure) failure()
}

function iterate(array, block) {
  array.forEach(block)
}

本质上这就像一个树语法:

parse =
  iterate input, value =>
    check isArray(value)
      call parse(value)
    check isNumber(value)
      check isEven(value)
        push(value)

你可以把它想象成 JSON:

{
  type: 'function',
  name: 'parse',
  children: [
    {
      type: 'iterate',
      children: [
        {
          type: 'check',
          checker: 'isArray',
          children: [
            {
              type: 'call',
              function: 'parse'
            }
          ]
        },
        {
          type: 'check',
          checker: 'isNumber',
          children: [
            {
              type: 'check',
              checker: 'isEven',
              children: [
                {
                  type: 'push',
                }
              ]
            }
          ]
        }
      ]
    }
  ]
}

基本上我真正的问题是我在自定义编程语言中有类似 JSON 的东西,我想将它转换成一个状态 机器而不是像我们这样的嵌套/递归函数调用 从这篇文章开始。

所以我的问题是,如何重写本文开头的递归函数系统,使其使用状态机?

您不必一路走来,从 JSON(在这个例子中是不完整的,因为我没有一个好的 简化示例)。

相反,只要您能从本文开头展示如何将这个递归或嵌套函数系统重写为状态机,就够了。

我的想象是这样的:

let transitions = [
  function1,
  function2,
  ...
]

let transition = transitions[0] // the start function in theory
let currentInput = input
let someStack = []
while (transition) {
  [currentInput, transition] = transition(someStack, currentInput)
}

但我很快就迷失了如何跟踪我们在输入中的位置,以及如何处理诸如迭代(如iterator)或“if 语句”(如check)。

function iterate(input) {
  // you can't do the recursive call here,
  // as the original while loop should handle this.
  // but, we should return some reference to the current input,
  // as well as the next transition.
}

如果必须选择一种语言,那么在 JavaScript 中执行此操作将是最简单的。但它甚至可以用伪代码来完成,以大致展示它是如何完成的。

【问题讨论】:

  • 问题不清楚。 “解析”一词意味着输入是一个字符串,并且您逐个字符地读取。在您的示例中,input 不是已经解析的 JS 值。请澄清。
  • 递归方法有什么问题?
  • @Olivier 通过解析我的意思是获取一些输入并将其转换为输出。我的示例使用嵌套数组系统(树),它接近我将“解析”的输入。但是,如果您可以更容易地想象一个字符串,那么任何一种方式都对我有用。递归方法不一定存在固有问题,我只是想知道如何以某种方式像过渡机器一样做到这一点。为了我的学习。
  • 去功能化可能与您的问题相关:youtube.com/watch?v=wppzFzzD4b8
  • 您好,您的赏金即将到期,如果您想要一个好的答案,您应该与 cmets 互动。 'isEven, isNumber isArray' 的集合是有限集吗?只有“isArray”才是“复合”?

标签: javascript algorithm parsing state-machine automata


【解决方案1】:

正如所说,您不能使用状态机,但如果我理解您的示例,您就不需要状态机。 您想要一种从数据构造谓词的方法; 这些谓词将适用于复杂的不规则数据结构。

  • 其中一些谓词会产生副作用。
  • 其中一些谓词会将操作传播到数据的子部分。
  • 我假设您有一种有限的方式来表达您的谓词,因此您可以组合一组有限的谓词来获得所需的谓词。

如果 Java 流有一个“递归”的 flatMap,那就足够了。

我是 Java 专家,所以我将尝试用 Java 编写。 主要思想是有一个 Op 函数接口,将一个 Object 映射到一个对象列表中。

然后你只需计算一个固定点。

interface Op{ List<Object> of(Object t); }
class A{
  Op push(List<Object> res){
    return o->{res.add(o);return List.of();};
    }  
  Op isArray(){
    return t->{
      if (!(t instanceof Object[] os)){ return List.of(); }
      return List.of(os);
      };
    }
  Op isNumber(Op more){
    return t->{
      if (!(t instanceof Integer i)){ return List.of(); }
      return more.of(i);
      };
    }
  Op isEven(Op more){
    return t->{
      if (!(t instanceof Integer i) || i%2==1){ return List.of(); }
      return more.of(i);
      };
    }
  void engine1(List<Op> query,Object input){//not recursive
    List<Object>edge=new ArrayList<>();
    List<Object>nextEdge=new ArrayList<>();
    edge.add(input);
    while(!edge.isEmpty()){
      for(var e:edge){
        for(var f:query){
          nextEdge.addAll(f.of(e));
          }
        }
      edge=nextEdge;
      nextEdge=new ArrayList<>();
      }
    }
  void example1() {
    List<Object> res=new ArrayList<>();
    List<Op> query=List.of(isArray(),isEven(push(res)));
    Object input= new Object[]{1,2,new Object[]{3,4},5,6};
    engine1(query,input);
    System.out.println(res);
    }
  public static void main(String[]arg) {
    new A().example1();//this would print 2,6,4
    }
  }

您的问题不清楚您是否可以接受此订单。 即访问副作用是广度优先 如果没有,我想我可以得到深度优先订单,但它必须在 12 小时左右,因为现在在我的时区已经很晚了。

【讨论】:

  • 这是一个好的开始,因为我不是 Java 专家,所以我将不得不花一些时间来研究它,但我认为它看起来是正确的方向。谢谢!
【解决方案2】:

这是不可能的。 state machine 无法识别/解析language with a recursive grammar,而只能识别/解析regular language。你需要一个stack machine

【讨论】:

  • 你能说明怎么做吗?这就是我一直在寻找的,我不想用不同的标题再次问同样的问题。如果我要求一个下推自动机,有人会说你不能用自动机构建输出(它只返回真/假)。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-09-19
  • 1970-01-01
相关资源
最近更新 更多