gpt4 book ai didi

javascript - 默认参数值未定义;这是一个 JavaScript 错误吗?

转载 作者:塔克拉玛干 更新时间:2023-11-02 21:11:19 24 4
gpt4 key购买 nike

下面是一个语法上有效的 javascript 程序 – 只是,它的行为与我们期望的不同。问题的标题应该可以帮助您将目光聚焦到问题领域

const recur = (...args) =>
({ type: recur, args })

const loop = f =>
{
let acc = f ()
while (acc.type === recur)
acc = f (...acc.args)
return acc
}

const repeat = n => f => x =>
loop ((n = n, f = f, x = x) => // The Problem Area
n === 0
? x
: recur (n - 1, f, f (x)))

console.time ('loop/recur')
console.log (repeat (1e6) (x => x + 1) (0))
console.timeEnd ('loop/recur')
// Error: Uncaught ReferenceError: n is not defined

如果我改为使用唯一 标识符,程序将完美运行

const recur = (...args) =>
({ type: recur, args })

const loop = f =>
{
let acc = f ()
while (acc.type === recur)
acc = f (...acc.args)
return acc
}

const repeat = $n => $f => $x =>
loop ((n = $n, f = $f, x = $x) =>
n === 0
? x
: recur (n - 1, f, f (x)))

console.time ('loop/recur')
console.log (repeat (1e6) (x => x + 1) (0)) // 1000000
console.timeEnd ('loop/recur') // 24 ms

只有这个没有意义。现在让我们谈谈不使用 $ 前缀的原始代码。

loop 的 lambda 被评估时,repeat 接收到的 n 在 lambda 的环境中可用。将内部 n 设置为外部 n 的值应该有效 shadow外层 n。但是,JavaScript 将此视为某种问题,并且内部 n 导致赋值 undefined

这对我来说似乎是一个错误,但我不擅长阅读规范,所以我不确定。

这是一个错误吗?

最佳答案

我想您已经知道为什么您的代码不起作用。默认参数的行为类似于递归的 let 绑定(bind)。因此,当您编写 n = n 时,您正在将新声明的(但尚未定义 )变量 n 分配给它自己。就我个人而言,我认为这是完全有道理的。

那么,您在评论中提到了 Racket,并评论了 Racket 如何允许程序员在 letletrec 之间进行选择。我喜欢将这些绑定(bind)与 Chomsky hierarchy 进行比较. let 绑定(bind)类似于常规语言。它不是很强大,但允许可变阴影。 letrec 绑定(bind)类似于递归可枚举语言。它可以做任何事情,但不允许变量阴影。

因为 letrec 可以做 let 可以做的所有事情,所以你根本不需要 let。一个典型的例子是 Haskell,它只有递归的 let 绑定(bind)(不幸的是称为 let 而不是 letrec)。现在的问题是像 Haskell 这样的语言是否也应该有 let 绑定(bind)。要回答这个问题,让我们看下面的例子:

-- Inserts value into slot1 or slot2
insert :: (Bool, Bool, Bool) -> (Bool, Bool, Bool)
insert (slot1, slot2, value) =
let (slot1', value') = (slot1 || value, slot1 && value)
(slot2', value'') = (slot2 || value', slot2 && value')
in (slot1', slot2', value'')

如果 Haskell 中的 let 不是递归的,那么我们可以将这段代码写成:

-- Inserts value into slot1 or slot2
insert :: (Bool, Bool, Bool) -> (Bool, Bool, Bool)
insert (slot1, slot2, value) =
let (slot1, value) = (slot1 || value, slot1 && value)
(slot2, value) = (slot2 || value, slot2 && value)
in (slot1, slot2, value)

那么为什么 Haskell 没有非递归的 let 绑定(bind)呢?好吧,使用不同的名称肯定有一些优点。作为一名编译器作者,我注意到这种编程风格类似于 single static assignment form其中每个变量名只使用一次。通过仅使用一次变量名,编译器可以更轻松地分析程序。

我认为这也适用于人类。使用不同的名称有助于阅读您的代码的人理解它。对于编写代码的人来说,重用现有名称可能更可取。但是,对于阅读代码的人来说,使用不同的名称可以防止由于一切看起来都一样而可能引起的任何混淆。事实上,Douglas Crockford(经常被吹捧的 JavaScript 大师)advocates context coloring解决类似的问题。


无论如何,回到手头的问题。我可以想到两种可能的方法来解决您眼前的问题。第一个解决方案是简单地使用不同的名称,这就是您所做的。第二种解决方案是模拟非递归 let 表达式。请注意,在 Racket 中,let 只是一个扩展为 left-left-lambda 表达式的宏。例如,考虑以下代码:

(let ([x 5])
(* x x))

let 表达式将宏扩展为以下 left-left-lambda 表达式:

((lambda (x) (* x x)) 5)

事实上,我们可以在 Haskell 中使用反向应用运算符 (&) 来做同样的事情:

import Data.Function ((&))

-- Inserts value into slot1 or slot2
insert :: (Bool, Bool, Bool) -> (Bool, Bool, Bool)
insert (slot1, slot2, value) =
(slot1 || value, slot1 && value) & \(slot1, value) ->
(slot2 || value, slot2 && value) & \(slot2, value) ->
(slot1, slot2, value)

本着同样的精神,我们可以通过手动“宏扩展”let 表达式来解决您的问题:

const recur = (...args) => ({ type: recur, args });

const loop = (args, f) => {
let acc = f(...args);
while (acc.type === recur)
acc = f(...acc.args);
return acc;
};

const repeat = n => f => x =>
loop([n, f, x], (n, f, x) =>
n === 0 ? x : recur (n - 1, f, f(x)));

console.time('loop/recur');
console.log(repeat(1e6)(x => x + 1)(0)); // 1000000
console.timeEnd('loop/recur');

在这里,我没有为初始循环状态使用默认参数,而是将它们直接传递给 loop。您可以将 loop 视为 Haskell 中的 (&) 运算符,它也进行递归。其实这段代码可以直接音译成Haskell:

import Prelude hiding (repeat)

data Recur r a = Recur r | Return a

loop :: r -> (r -> Recur r a) -> a
loop r f = case f r of
Recur r -> loop r f
Return a -> a

repeat :: Int -> (a -> a) -> a -> a
repeat n f x = loop (n, f, x) (\(n, f, x) ->
if n == 0 then Return x else Recur (n - 1, f, f x))

main :: IO ()
main = print $ repeat 1000000 (+1) 0

如您所见,您根本不需要letlet 可以完成的所有事情也可以通过 letrec 完成,如果你真的想要变量阴影,那么你可以手动执行宏扩展。在 Haskell 中,你甚至可以更进一步,使用 The Mother of all Monads 使你的代码更漂亮。 .

关于javascript - 默认参数值未定义;这是一个 JavaScript 错误吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/46135908/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com