【问题标题】:Javascript chaining a method to the result of recursive callsJavascript将方法链接到递归调用的结果
【发布时间】:2017-11-22 04:30:16
【问题描述】:

我试图从递归调用中返回一个字符串,然后对其进行切片,但是切片的行为很奇怪。

给定

let first = arr => arr.slice(0, 1)
let rest = arr => arr.slice(1)

let join = function join(arr, connector) { 
  return !arr.length ? '' : first(arr) + connector + join(rest(arr), connector)
}

let nums = [1, 2, 34, 45, 100]

其中 join 从数组值中创建一个字符串,并带有一个连接符 as

join(nums, '-')

返回

"1-2-34-45-100-"

然后切掉最后一个'-'

"1-2-34-45-100-".slice(0, -1)

返回

"1-2-34-45-100"

function f() {
  return ("1-2-34-45-100-").slice(0, -1)
}
f()

返回 “1-2-34-45-100”

足够简单。那么为什么要在上述递归调用的结果中添加一个切片,比如

let join = function join(arr, connector) { 
    return ( !arr.length ? '' : first(arr) + connector + join(rest(arr), connector) )
      .slice(0, -1); 
}

生产这个?

join(nums, '-')
// -> "1-2-34-45"
// shouldn't this be "1-2-34-45-100" ?

甚至是陌生人,

join([1,2,3,4,5,6,7,8,9,10], '-')
// -> "1-2-3-4-5-6"

切片是否参与递归调用?

【问题讨论】:

  • 您需要添加一些console.logs,这样您才能看到正在发生的事情 - 或者更好的是,学习如何使用 javascript 调试器,例如 Chrome 附带的调试器。
  • 你不认为 slice() 每次递归都会运行吗?它在每次通话时运行
  • @epascarello 这是我的假设。这是不正确的吗?如果是这样,我仍然无法对结果进行推理。
  • 因为代码是这样的:function join (...) { var x = ( !arr.length ? '' : first(arr) + connector + join(rest(arr), connector) ) .slice(0, -1); return x }
  • 对于 join() 的递归似乎也很奇怪......

标签: javascript recursion slice


【解决方案1】:

首先,你的first 函数有问题

// first :: [a] -> [a] WUPS
let first = arr =>
  arr.slice(0, 1)

first 应该返回第一个元素,不是包含第一个元素的单元素数组

// first :: [a] -> a
const first = xs =>
  xs[0]

接下来,我们考虑您的函数的完整domain(输入)。您的函数接受一个字符串数组。该数组可以包含 0、1 或更多字符串,我们需要您的函数适用于所有情况——codomain(输出)是字符串类型; join 应该总是返回一个字符串

// contract
join ([])      == ''
join ([a])     == a
join ([a,b])   == a + '-' + b
join ([a,b,c]) == a + '-' + b + '-' + c

在递归函数中对此进行编码在 JavaScript 中是微不足道的 - 请注意,无需跟踪数组索引或担心递增迭代器 - 只需考虑履行该合同

const first = xs =>
  xs[0]

const rest = xs =>
  xs.slice (1)

const join = xs => {
  switch (xs.length) {
    case 0:   return ''
    case 1:   return first (xs)
    default:  return first (xs) + '-' + join (rest (xs))
  }
}

console.log (join ([]))               // ''
console.log (join (['a']))            // 'a'
console.log (join (['a', 'b']))       // 'a-b'
console.log (join (['a', 'b', 'c']))  // 'a-b-c'

当然,如果我们允许用户指定连接字符串,我们的功能可以得到很大的改进

const first = xs =>
  xs[0]

const rest = xs =>
  xs.slice (1)

const join = (y, xs) => {
  switch (xs.length) {
    case 0:   return ''
    case 1:   return first (xs)
    default:  return first (xs) + y + join (y, rest (xs))
  }
}

console.log (join ('&', []))               // ''
console.log (join ('&', ['a']))            // 'a'
console.log (join ('&', ['a', 'b']))       // 'a&b'
console.log (join ('&', ['a', 'b', 'c']))  // 'a&b&c'

【讨论】:

  • naomik 我读了很多你的帖子——总是好东西。非常感谢。
【解决方案2】:

您将slice 放入join 函数中,每次运行join() 时都会导致slice

而且因为你在 join 函数中进行递归,slice 将被多次调用。

尝试在完成join() 时调用切片。它会起作用的。

let first = arr => arr.slice(0, 1)
let rest = arr => arr.slice(1)

let join = function join(arr, connector) { 
    return ( !arr.length ? '' : first(arr) + connector + join(rest(arr), connector) )
}

join([1,2,3,4,5,6,7,8,9,10], '-').slice(0, -1)

更新

根据你的关心,你可以这样写代码:

const join = function(arr, connector) {
  const first = arr => arr.slice(0, 1)
  const rest = arr => arr.slice(1)

  const joinChain = function(arr, connector) {
    return ( !arr.length ? '' : first(arr) + connector + joinChain(rest(arr), connector) );
  }
  return joinChain(arr, connector).slice(0, -1);
}

join([1,2,3,4,5,6,7,8,9,10], '-')

【讨论】:

  • 谢谢 - 你可以直接链接到 Sting.prototype,但我们想要一个可以完成所有工作的连接函数。
  • @Harold 基于您的关注,我更新了代码。使用连接函数来解决问题。
【解决方案3】:

您应该只调整逻辑以避免尾随连接符。

let first = arr => arr.slice(0, 1)
let rest = arr => arr.slice(1)

let join = function join(arr, conn) { 
  return !arr.length ? "" :
          first(arr) + (arr.length === 1 ? "" : (conn + join(rest(arr), conn)))
}

let nums = [1, 2, 34, 45, 100]

console.log(join(nums, "-"));

嵌套函数上的“剩余参数”可以使这更简单一些。

let join = function join(arr, conn) {
  return j(...arr);

  function j(head, ...rest) {
    return (head || "") + (rest.length ? (conn + j(...rest, conn)) : "")
  }
}

let nums = [1, 2, 34, 45, 100]

console.log(join(nums, "-"));

【讨论】:

    猜你喜欢
    • 2022-11-22
    • 1970-01-01
    • 1970-01-01
    • 2023-03-14
    • 2017-10-22
    • 2018-09-25
    • 2016-04-10
    • 1970-01-01
    • 2023-04-10
    相关资源
    最近更新 更多