- iOS/Objective-C 元类和类别
- objective-c - -1001 错误,当 NSURLSession 通过 httpproxy 和/etc/hosts
- java - 使用网络类获取 url 地址
- ios - 推送通知中不播放声音
下面是一个语法上有效的 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 如何允许程序员在 let
和 letrec
之间进行选择。我喜欢将这些绑定(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
如您所见,您根本不需要let
。 let
可以完成的所有事情也可以通过 letrec
完成,如果你真的想要变量阴影,那么你可以手动执行宏扩展。在 Haskell 中,你甚至可以更进一步,使用 The Mother of all Monads 使你的代码更漂亮。 .
关于javascript - 默认参数值未定义;这是一个 JavaScript 错误吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/46135908/
我已经使用 vue-cli 两个星期了,直到今天一切正常。我在本地建立这个项目。 https://drive.google.com/open?id=0BwGw1zyyKjW7S3RYWXRaX24tQ
您好,我正在尝试使用 python 库 pytesseract 从图像中提取文本。请找到代码: from PIL import Image from pytesseract import image_
我的错误 /usr/bin/ld: errno: TLS definition in /lib/libc.so.6 section .tbss mismatches non-TLS reference
我已经训练了一个模型,我正在尝试使用 predict函数但它返回以下错误。 Error in contrasts<-(*tmp*, value = contr.funs[1 + isOF[nn]])
根据Microsoft DataConnectors的信息我想通过 this ODBC driver 创建一个从 PowerBi 到 PostgreSQL 的连接器使用直接查询。我重用了 Micros
我已经为 SoundManagement 创建了一个包,其中有一个扩展 MediaPlayer 的类。我希望全局控制这个变量。这是我的代码: package soundmanagement; impo
我在Heroku上部署了一个应用程序。我正在使用免费服务。 我经常收到以下错误消息。 PG::Error: ERROR: out of memory 如果刷新浏览器,就可以了。但是随后,它又随机发生
我正在运行 LAMP 服务器,这个 .htaccess 给我一个 500 错误。其作用是过滤关键字并重定向到相应的域名。 Options +FollowSymLinks RewriteEngine
我有两个驱动器 A 和 B。使用 python 脚本,我在“A”驱动器中创建一些文件,并运行 powerscript,该脚本以 1 秒的间隔将驱动器 A 中的所有文件复制到驱动器 B。 我在 powe
下面的函数一直返回这个错误信息。我认为可能是 double_precision 字段类型导致了这种情况,我尝试使用 CAST,但要么不是这样,要么我没有做对...帮助? 这是错误: ERROR: i
这个问题已经有答案了: Syntax error due to using a reserved word as a table or column name in MySQL (1 个回答) 已关闭
我的数据库有这个小问题。 我创建了一个表“articoli”,其中包含商品的品牌、型号和价格。 每篇文章都由一个 id (ID_ARTICOLO)` 定义,它是一个自动递增字段。 好吧,现在当我尝试插
我是新来的。我目前正在 DeVry 在线学习中级 C++ 编程。我们正在使用 C++ Primer Plus 这本书,到目前为止我一直做得很好。我的老师最近向我们扔了一个曲线球。我目前的任务是这样的:
这个问题在这里已经有了答案: What is an undefined reference/unresolved external symbol error and how do I fix it?
我的网站中有一段代码有问题;此错误仅发生在 Internet Explorer 7 中。 我没有在这里发布我所有的 HTML/CSS 标记,而是发布了网站的一个版本 here . 如您所见,我在列中有
如果尝试在 USB 设备上构建 node.js 应用程序时在我的树莓派上使用 npm 时遇到一些问题。 package.json 看起来像这样: { "name" : "node-todo",
在 Python 中,您有 None单例,在某些情况下表现得很奇怪: >>> a = None >>> type(a) >>> isinstance(a,None) Traceback (most
这是我的 build.gradle (Module:app) 文件: apply plugin: 'com.android.application' android { compileSdkV
我是 android 的新手,我的项目刚才编译和运行正常,但在我尝试实现抽屉导航后,它给了我这个错误 FAILURE: Build failed with an exception. What wen
谁能解释一下?我想我正在做一些非常愚蠢的事情,并且急切地等待着启蒙。 我得到这个输出: phpversion() == 7.2.25-1+0~20191128.32+debian8~1.gbp108
我是一名优秀的程序员,十分优秀!