递归数组串联错误JavaScript

如何解决递归数组串联错误JavaScript

任务:给定格式为foo(bar)的字符串,请在括号内将字符串取反。

我的解决方案有一个错误,我无法理解其原因。

这是我的代码:

    const inputs = ["foo(bar)","(bar)","foo(bar)blim","foo(foo(bar))blim","foo(foo(bar)ddas)blim","foo(foo(b(tu)ar)ddas)blim"]
const expected = ["foorab","rab","foorabblim","foobaroofblim","foosaddbaroofblim","foosaddbutaroofblim"]

function reverseParans(input) {
    firstLeftParans = input.indexOf('(')
    lastRightParans = input.lastIndexOf(')')

    if (firstLeftParans == -1) {
        if (lastRightParans > -1) {
            return ['<MALFORMED>']
        } else {
            return input.reverse()
        }
    } else {
        if (lastRightParans == -1) {
            return ['<MALFORMED>']
        } else {
            left = input.slice(0,firstLeftParans)
            right = input.slice(lastRightParans+1).slice()
            middle = reverseParans(input.slice(firstLeftParans + 1,lastRightParans))
            return [...left,...middle,...right].reverse()
        }
    }
}

function process(str) {
    let input = str.split('');
    let firstLeftParans = input.indexOf('(');
    let lastRightParans = input.lastIndexOf(')');

    if (firstLeftParans == -1 && lastRightParans == -1) {
        return input
    } else if ((firstLeftParans > -1 && lastRightParans == -1) || (lastRightParans > -1 && firstLeftParans == -1)) {
        return "<MALFORMED>"
    }
    result = input.slice(0,firstLeftParans).concat(reverseParans(input.slice(firstLeftParans + 1,lastRightParans)),input.slice(lastRightParans + 1))
    return result.join('')
}

除最后一个带有嵌套级别的输入外,所有输入均成功。

Input: foo(foo(b(tu)ar)ddas)blim   Result: foorabutarbblim   Expected:  foosaddbutaroofblim  Pass: false

我认为我在进行递归调用时错误地变异了一个值,但是我不确定在哪里。

解决方法

当存在多套未套用的括号时,关于此问题的其他一些答案会产生错误的结果-

console.log(process("hello(world)yes(no)yang(yin)"))
// helloniy(gnaynosey)dlrow

console.log(process("ab(cd(ef(gh)))ij(k(lm)n)opqr(stu(vw)x)yz"))
// abxefn<MALFORMED>ji)))hgopqr(stu(vwdcyz

// OH NO!

lex

让我们尝试另一种解决问题的方法。首先lexer创建一个词素流-

function* lexer(s = "")
{ const leftParen =
    { type: "leftParen" }

  const rightParen =
    { type: "rightParen" }

  const str = value =>
    ({ type: "str",value })
  
  let r =
    ""
  
  for (const c of s)
    if (c === "(")
      (yield str(r),yield leftParen,r = "")
    else if (c === ")")
      (yield str(r),yield rightParen,r = "")
    else
      r = r + c

  yield str(r)
}

让我们看看lexer的工作原理-

lexer("this(is(very(nested)))but(nothere)") // => ...
{ type: "str",value: "this" }
{ type: "leftParen" }
{ type: "str",value: "is" }
{ type: "leftParen" }
{ type: "str",value: "very" }
{ type: "leftParen" }
{ type: "str",value: "nested" }
{ type: "rightParen" }
{ type: "str",value: "" }
{ type: "rightParen" }
{ type: "str",value: "but" }
{ type: "leftParen" }
{ type: "str",value: "nothere" }
{ type: "rightParen" }
{ type: "str",value: "" }

解析

接下来,parser接受词素并生成抽象语法树-

function parser(lexemes)
{ const concat = _ =>
    ({ type: "concat",values: [] })
  
  const rev = _ =>
    ({ type: "rev",values: [] })

  const r =
    [ concat() ]

  for (const t of lexemes)
    if (t.type === "str")
      r[0].values.unshift(t)
    else if (t.type === "leftParen")
      r.unshift(rev())
    else if (t.type === "rightParen")
      if (r.length <= 1) 
        throw Error("unexpected ')'")
      else
        r[1].values.unshift(r.shift())
    else
      throw Error("unexpected lexeme")

  if (r.length > 1)
    throw Error("expected ')'")
  else
    return r[0]
}

现在让我们看看parser的效果-

parser(lexer("this(is(very(nested)))but(nothere)")) // => ...
{ type: "concat",values: 
    [ { type: "str",value: "" },{ type: "rev",values:
          [ { type: "str",value: "nothere" }
          ]
      },{ type: "str",value: "but" },values: 
          [ { type: "str",values:
                [ { type: "str",values:
                      [ { type: "str",value: "nested" } 
                      ]
                  },value: "very" }
                ]
            },value: "is" }
          ]
      },value: "this" }
    ]
}

评估

最后eval递归地评估AST并给出一个值-

function eval(e)
{ if (e.type === "str")
    return e.value
  else if (e.type === "concat")
    return reverse(e.values).map(eval).join("")
  else if (e.type === "rev")
    return e.values.map(_ => reverse(eval(_))).join("")
  else
    throw Error(`unexpected expression: ${e}`)
}

让我们看看eval如何处理我们的示例-

eval(parser(lexer("this(is(very(nested)))but(nothere)"))) // => ...
"thisverydetsensibuterehton"

过程

现在process只是lexerparsereval的组合-

const process = (s = "") =>
  eval(parser(lexer(s)))

该程序的工作方式与以前一样-

const inputs =
  ["foo(bar)","(bar)","foo(bar)blim","foo(foo(bar))blim","foo(foo(bar)ddas)blim","foo(foo(b(tu)ar)ddas)blim"]

const expected =
  ["foorab","rab","foorabblim","foobaroofblim","foosaddbaroofblim","foosaddbutaroofblim"]

const result =
  inputs.map(process) // <-- call process for each input

console.log(expected.join(","))
console.log(result.join(","))

输出-

foorab,rab,foorabblim,foobaroofblim,foosaddbaroofblim,foosaddbutaroofblim
foorab,foosaddbutaroofblim

除了现在,我们的程序适用于更复杂的输入-

console.log(process("hello(world)yes(no)yang(yin)"))
// hellodlrowyesonyangniy

console.log(process("ab(cd(ef(gh)))ij(k(lm)n)opqr(stu(vw)x)yz"))
// abefhgdcijnlmkopqrxvwutsyz

您刚刚编写了自己的编程语言!

char = "a" | "b" | "c" | "d" ...
     | "A" | "B" | "C" | "D" ...
     | "1" | "2" | "3" | "4" ...
     | "!" | "@" | "#" | "$" ...
     | ...

string = ""
       | char + string

expr = string
     | expr + "(" + expr + ")" + expr

在您自己的浏览器中验证结果-

const reverse = (s = "") =>
  s.length > 1
    ? reverse(s.slice(1)).concat(s.slice(0,1))
    : s

function* lexer(s = "")
{ const leftParen =
    { type: "leftParen" }

  const rightParen =
    { type: "rightParen" }

  const str = value =>
    ({ type: "str",r = "")
    else
      r = r + c

  yield str(r)
}

function parser(lexemes)
{ const concat = _ =>
    ({ type: "concat",values: [] })

  const r =
    [ concat() ]

  for (const t of lexemes)
    if (t.type === "str")
      r[0].values.unshift(t)
    else if (t.type === "leftParen")
      r.unshift(rev())
    else if (t.type === "rightParen")
      if (r.length <= 1) 
        throw Error("unexpected ')'")
      else
        r[1].values.unshift(r.shift())
    else
      throw Error("unexpected lexeme")

  if (r.length > 1)
    throw Error("expected ')'")
  else
    return r[0]
}

function eval(e)
{ if (e.type === "str")
    return e.value
  else if (e.type === "concat")
    return reverse(e.values).map(eval).join("")
  else if (e.type === "rev")
    return e.values.map(_ => reverse(eval(_))).join("")
  else
    throw Error(`unexpected expression: ${e}`)
}

const process = (s = "") =>
  eval(parser(lexer(s)))
  
const inputs =
  ["foo(bar)","foosaddbutaroofblim"]

console.log(expected.join(","))
console.log(inputs.map(process).join(","))
console.log("hellodlrowyesonyangniy")
console.log(process("hello(world)yes(no)yang(yin)"))
console.log("abefhgdcijnlmkopqrxvwutsyz")
console.log(process("ab(cd(ef(gh)))ij(k(lm)n)opqr(stu(vw)x)yz"))

,

“谢谢”的第二个答案可以教给您很多有关编写解析器的知识。我强烈推荐它。

这里的基本问题不能用任何简单的正则表达式解决。括号内的语言不是常规语言;它是上下文无关的语言。但是,我们可以编写一个相当简单的版本,该版本使用采用正则表达式测试和替换的递归调用:

// or use the recursive version from @Thankyou
const reverse = (s) =>
  [...s] .reverse () .join ('')

const parens = /\(([^)(]*)\)/

const process = (s) => 
  parens .test (s)
    ? process (s .replace (parens,(_,inner) => reverse (inner)))
    : s


// Original sample cases
const inputs =   ["foo(bar)","foo(foo(b(tu)ar)ddas)blim"]
const expected = ["foorab","foosaddbutaroofblim"      ]

console .log (expected .join (","))
console .log (inputs .map (process) .join (","))

//  Extended sample cases from @Thankyou
console. log ("hellodlrowyesonyangniy")
console .log (process ("hello(world)yes(no)yang(yin)"))
console .log ("abefhgdcijnlmkopqrxvwutsyz")
console .log (process ("ab(cd(ef(gh)))ij(k(lm)n)opqr(stu(vw)x)yz"))

parens是一个正则表达式,它与一个以开放括号开头的子字符串匹配,然后捕获一组零个或多个不包含任何封闭或开放括号的字符,然后是封闭括号。它像这样分解:

// const parens = /\(([^)(]*)\)/
//                /            /  -- a regular expression matching a sub-string
//                 \(             -- starting with an open parenthesis 
//                   (      )     -- then capturing a group of 
//                         *      -- zero or more
//                    [^  ]       -- characters not including 
//                      )(        -- any close or open parentheses
//                           \)   -- followed by a close parenthesis

换句话说,我们可以使用它来找到任何嵌套括号的最内层,并捕获它们之间的内容。

函数process使用它来测试是否有任何这样的子字符串。如果没有,我们就完成了,我们将字符串原样返回。如果有的话,我们用内容的相反版本替换该组括号,然后用结果递归调用process

,

您弄乱了范围,因为您没有将变量声明为reverseParans的局部变量。修改后的代码有效:

const inputs = ["foo(bar)","foosaddbutaroofblim"]

function reverseParans(input) {
    let firstLeftParans = input.indexOf('(')
    let lastRightParans = input.lastIndexOf(')')

    if (firstLeftParans == -1) {
        if (lastRightParans > -1) {
            return ['<MALFORMED>']
        } else {
            return input.reverse()
        }
    } else {
        if (lastRightParans == -1) {
            return ['<MALFORMED>']
        } else {
            let left = input.slice(0,firstLeftParans)
            let right = input.slice(lastRightParans+1).slice()
            let middle = reverseParans(input.slice(firstLeftParans + 1,lastRightParans))
            return [...left,...middle,...right].reverse()
        }
    }
}

function process(str) {
    let input = str.split('');
    let firstLeftParans = input.indexOf('(');
    let lastRightParans = input.lastIndexOf(')');

    if (firstLeftParans == -1 && lastRightParans == -1) {
        return input
    } else if ((firstLeftParans > -1 && lastRightParans == -1) || (lastRightParans > -1 && firstLeftParans == -1)) {
        return "<MALFORMED>"
    }
    result = input.slice(0,firstLeftParans).concat(reverseParans(input.slice(firstLeftParans + 1,lastRightParans)),input.slice(lastRightParans + 1))
    return result.join('')
}
,

通过数学归纳递归可以简化您的程序。下面编号的注释对应于代码中的数字-

  1. 如果找不到左括号和右括号;返回输入字符串s
  2. (归纳),如果仅找到一个父级;返回格式错误的字符串结果
  3. (归纳)左派和右派都找到了;返回左括号前的字符串部分,加上递归结果,再加上右括号后的字符串部分
function process(s = "")
{ const l = s.indexOf("(")          // "left"
  const r = s.lastIndexOf(")")      // "right"
  if (l === -1 && r === -1)
    return s                        // 1
  else if (l === -1 || r === -1)
    return "<MALFORMED>"            // 2
  else
    return s.substring(0,l)        // 3
      + reverse(process(s.substring(l + 1,r)))
      + s.substr(r + 1)
}

我们可以将reverse定义为-

const reverse = (s = "") =>
  s.length
    ? reverse(s.substr(1)) + s.substr(0,1)
    : ""
const inputs =
  ["foo(bar)",foosaddbutaroofblim

展开代码段以在浏览器中验证结果-

const reverse = (s = "") =>
  s.length
    ? reverse(s.substr(1)) + s.substr(0,1)
    : ""

function process(s = "")
{ const l = s.indexOf("(")
  const r = s.lastIndexOf(")")
  if (l === -1 && r === -1)
    return s
  else if (l === -1 || r === -1)
    return "<MALFORMED>"
  else
    return s.substring(0,l)
      + reverse(process(s.substring(l + 1,r)))
      + s.substr(r + 1)
}

const inputs =
  ["foo(bar)","))

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


依赖报错 idea导入项目后依赖报错,解决方案:https://blog.csdn.net/weixin_42420249/article/details/81191861 依赖版本报错:更换其他版本 无法下载依赖可参考:https://blog.csdn.net/weixin_42628809/a
错误1:代码生成器依赖和mybatis依赖冲突 启动项目时报错如下 2021-12-03 13:33:33.927 ERROR 7228 [ main] o.s.b.d.LoggingFailureAnalysisReporter : *************************** APPL
错误1:gradle项目控制台输出为乱码 # 解决方案:https://blog.csdn.net/weixin_43501566/article/details/112482302 # 在gradle-wrapper.properties 添加以下内容 org.gradle.jvmargs=-Df
错误还原:在查询的过程中,传入的workType为0时,该条件不起作用 &lt;select id=&quot;xxx&quot;&gt; SELECT di.id, di.name, di.work_type, di.updated... &lt;where&gt; &lt;if test=&qu
报错如下,gcc版本太低 ^ server.c:5346:31: 错误:‘struct redisServer’没有名为‘server_cpulist’的成员 redisSetCpuAffinity(server.server_cpulist); ^ server.c: 在函数‘hasActiveC
解决方案1 1、改项目中.idea/workspace.xml配置文件,增加dynamic.classpath参数 2、搜索PropertiesComponent,添加如下 &lt;property name=&quot;dynamic.classpath&quot; value=&quot;tru
删除根组件app.vue中的默认代码后报错:Module Error (from ./node_modules/eslint-loader/index.js): 解决方案:关闭ESlint代码检测,在项目根目录创建vue.config.js,在文件中添加 module.exports = { lin
查看spark默认的python版本 [root@master day27]# pyspark /home/software/spark-2.3.4-bin-hadoop2.7/conf/spark-env.sh: line 2: /usr/local/hadoop/bin/hadoop: No s
使用本地python环境可以成功执行 import pandas as pd import matplotlib.pyplot as plt # 设置字体 plt.rcParams[&#39;font.sans-serif&#39;] = [&#39;SimHei&#39;] # 能正确显示负号 p
错误1:Request method ‘DELETE‘ not supported 错误还原:controller层有一个接口,访问该接口时报错:Request method ‘DELETE‘ not supported 错误原因:没有接收到前端传入的参数,修改为如下 参考 错误2:cannot r
错误1:启动docker镜像时报错:Error response from daemon: driver failed programming external connectivity on endpoint quirky_allen 解决方法:重启docker -&gt; systemctl r
错误1:private field ‘xxx‘ is never assigned 按Altʾnter快捷键,选择第2项 参考:https://blog.csdn.net/shi_hong_fei_hei/article/details/88814070 错误2:启动时报错,不能找到主启动类 #
报错如下,通过源不能下载,最后警告pip需升级版本 Requirement already satisfied: pip in c:\users\ychen\appdata\local\programs\python\python310\lib\site-packages (22.0.4) Coll
错误1:maven打包报错 错误还原:使用maven打包项目时报错如下 [ERROR] Failed to execute goal org.apache.maven.plugins:maven-resources-plugin:3.2.0:resources (default-resources)
错误1:服务调用时报错 服务消费者模块assess通过openFeign调用服务提供者模块hires 如下为服务提供者模块hires的控制层接口 @RestController @RequestMapping(&quot;/hires&quot;) public class FeignControl
错误1:运行项目后报如下错误 解决方案 报错2:Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.8.1:compile (default-compile) on project sb 解决方案:在pom.
参考 错误原因 过滤器或拦截器在生效时,redisTemplate还没有注入 解决方案:在注入容器时就生效 @Component //项目运行时就注入Spring容器 public class RedisBean { @Resource private RedisTemplate&lt;String
使用vite构建项目报错 C:\Users\ychen\work&gt;npm init @vitejs/app @vitejs/create-app is deprecated, use npm init vite instead C:\Users\ychen\AppData\Local\npm-