LispAllegro Common Lisp如何在Lambda中使用带'vs#'的变量 阅读器宏词汇环境特殊运算符强制类型名称解析,编译器和后期绑定

如何解决LispAllegro Common Lisp如何在Lambda中使用带'vs#'的变量 阅读器宏词汇环境特殊运算符强制类型名称解析,编译器和后期绑定

我希望有人能解释为什么测试1-5起作用,而测试6却不起作用。我以为用lambda前面加上一个lambda并在其中使用#都会返回指向该函数的指针,唯一的区别是#'将首先编译它。

(test-1 2)     ; --> (1 4 9)
(test-2 2)     ; --> (1 4 9)
(test-3 2)     ; --> (1 4 9)
(test-4 2)     ; --> (1 4 9)
(test-5 2)     ; --> (1 4 9)
(test-6 2)     ; --> Error: Attempt to take the value of the unbound variable `Y'. [condition type: UNBOUND-VARIABLE]

我正在使用Franz Industries Allegro Common Lisp的免费版本。以下是输出:

sum(unlist(Map(function(x,y) x[y],mydata[c("FTHG","FTAG")],list(mydata$HomeTeam=="Aston Villa",mydata$AwayTeam=="Aston Villa"))))

解决方法

首先,您应该知道测试1-4符合Common Lisp,而测试5和6则不符合。我相信Allegro完全可以执行5和6的工作,但是它的工作超出了标准。讨论这个问题的标准的一点是像mapcar这样的函数的定义,该函数以函数指示符作为参数,以及function designator的定义:

功能指示符 n 功能指示符;也就是说,一个对象代表一个函数,并且是以下之一:符号(表示在全局环境中由该符号命名的函数)或 function (表示本身)。如果将符号用作功能指示符,但它没有作为函数的全局定义,或者具有作为宏或特殊形式的全局定义,则后果是不确定的。 [...]

由此可以清楚地看出,像(lambda (...) ...)这样的列表不是功能指示符:它只是一个列表,其汽车恰好是lambda。 Allegro正在做的事情是注意到该列表实际上是可以转化为函数并可以执行此操作的东西。

好吧,让我们写一个mapcar的版本,该版本可以执行Allegro的操作:

(defun mapcar/coercing (maybe-f &rest lists)
  (apply #'mapcar (coerce maybe-f 'function) lists))

这仅使用coerce,它是一个知道如何将此类列表转换为函数的函数。如果其参数已经是一个函数,则coerce仅返回它。

现在我们可以使用此功能编写两个测试:

(defun test-5/coercing (y)
  (mapcar/coercing '(lambda (x) (expt x 2))
                   '(1 2 3)))

(defun test-6/coercing (y)
  (mapcar/coercing '(lambda (x) (expt x y))
                   '(1 2 3)))

因此,在序言之后,test-6/explicit为什么不起作用?答案是,Common Lisp(除了特殊变量外)是词法范围的词法范围只是一种奇特的说法,即可用的绑定(变量)是准确的,只有您可以通过查看程序源看到的绑定。 (除非是CL的特殊绑定,我将忽略,因为这里没有。)

因此,考虑到这一点,请考虑test-6/coercing,尤其是对mapcar/coercing的调用:在该调用中,coerce必须将列表(lambda (x) (expt z y))转换为一个函数。所以做到了。但是它返回的函数不会绑定y,并且其中没有可见的y绑定:该函数使用y'free'。

唯一可行的方法是coerce为我们构造的函数是动态寻找y的绑定。嗯,这就是动态范围语言的功能,但是CL并不是动态范围的。

也许可以更清楚地说明这一点,是认识到我们可以直接从函数中取消函数创建:

(defun test-7 (y f)
  (mapcar f '(1 2 3)))

> (test-7 1 (coerce '(lambda (x) (expt x y)) 'function))

很明显,这无法在词汇范围内的语言中工作。

那么,测试1-4如何工作?

首先,这里实际上只有两个测试。在CL中,lambda是宏,(lambda (...) ...)完全等效于(function (lambda (...) ...))。当然,#'(lambda (...) ...)(function (lambda (...) ...))也是:它只是一个读取宏。

(function ...)是一个神奇的东西(一种特殊形式),上面写着“这是一个函数”。 function的重要之处在于它不是一个函数:这是一件极不可思议的事情,它告诉评估者(或编译器)其参数是当前词法上下文中函数的描述,例如,

(let ((x 1))
  (function (lambda (y) (+ x y))))

此函数创建的x是受x约束的let。因此,在您的测试2和4(相同)中:

(defun test-4 (y)
  (mapcar (function (lambda (x) (expt x y)))
      '(1 2 3)))

创建的函数所引用的y的绑定是y的绑定,该绑定在词汇上是可见的,这是test-4本身的参数。

,

让我们添加一个y参数,以避免关闭变量并查看我们要处理的值类型:

USER> (type-of #'(lambda (x y) (expt x y)))
FUNCTION

USER> (type-of (lambda (x y) (expt x y)))
FUNCTION

USER> (type-of '(lambda (x y) (expt x y)))
CONS

如您所见,前两个 lambda-like 形式被评估为函数,而第三个形式被评估为cons单元。就Lisp而言,第三个参数只是一棵没有意义的符号树。

阅读器宏

我以为用lambda引一个lambda并在其前面使用#'都会返回该函数的指针,唯一的区别是#'将首先编译它。

让我们回到定义,'#'是阅读器宏,分别是Single-QuoteSharpsign Single-Quote。它们位于其他形式的前面,例如'f读为(quote f),而#'f读为(function f)。在读取时,f和生成的表格只是未评估的数据。

我们将在下面看到两个特殊运算符的解释方式,但真正重要的是词法范围,所以我们打开一个括号。

词汇环境

Lexical environments是在代码某些点有效的绑定集。当您评估letflet时,它将通过新的绑定丰富当前环境。在表达式上调用EVAL时,即使对eval本身的调用是在非空环境中,您也将从空词法环境开始求值。

xeval期间未绑定:

(let ((x 3)) (eval '(list x))) ;; ERROR

在这里,我们构建一个让eval进行评估的租赁交易:

(eval '(let ((x 3)) (list x)))
=> (3)

这就是词汇环境下的速成课程。

特殊运算符

功能

特殊运算符FUNCTION接受一个参数,该参数可以是函数的名称(符号或setf)或 lambda表达式;特别是:

function的值是当前词汇环境中name的功能值。

此处,lambda表达式是在当前词法环境中求值的,这意味着它可以引用lambda表达式之外的变量。那就是闭包的定义,它们捕获周围的绑定。

NB。您无需在lambda前面加上#',因为存在一个名为(lambda ...)的宏,它可以将扩展扩展到(function (lambda ...))中。看起来这可能会永远递归扩展,但事实并非如此:首先扩展宏,使(lambda ...)变为(function (lambda ...)),然后特殊运算符function知道如何求值lambda表达式本身。 这意味着(lambda ...)#'(lambda ...)是等效的。特别要注意的是,此时并没有编译一种形式,编译器在宏扩展后将看到相同的表达式。

引用

特殊运算符QUOTE(quote f)评估为f,其中f本身未评估。在test-5test-6中,没有函数,只是一个可以被解释为代码的未经评估的结构化表达式。

强制类型

现在,某些功能,例如MAPCAR用于应用功能。请注意,规范说明function参数是一个函数 designator

function ---函数的代号,必须使用与列表一样多的参数。

类型的指定符不一定是该类型的值,而可以是可以强制转换为该类型的值。有时用户想要指定路径名,然后输入一个字符串,但是字符串不是pathname类型的值:系统必须将字符串转换为路径名。

Common Lisp使用有关如何将值转换为其他值的规则定义了COERCE函数。对于您而言,mapcar首先执行(coerce (lambda ...) 'function)。定义如下:

如果result-type是function,而object是lambda表达式,则结果是null词法环境中object的闭包。

因此,该值是在 null词法环境中求值的,因此它无法访问周围的绑定; y是您的lambda表达式中的一个自由变量,由于它是在空环境中求值的,因此它是不受约束的。这就是test-5通过但test-6失败的原因。

名称解析,编译器和后期绑定

在引用函数#'f时写'ff是有区别的,其中f是符号:在第一种情况下,表达式的计算结果为类型为function的对象,在第二种情况下,您仅评估符号。

此函数的名称解析可以根据编译器及其工作方式而改变。使用符号作为功能指示符,甚至不需要定义功能,在必须将符号强制作为功能时解析名称。

编写#'f时,某些编译器可能会删除一个间接级别,而直接使您的代码跳转到与该函数关联的代码,而不必在运行时解析名称。

但是,这也意味着,使用此类编译器(例如SBCL),您需要在函数重定义上重新编译某些调用站点,就像该函数被声明为 inline 一样,否则某些旧代码仍然会请参考#'f的先前定义。刚开始时,这并不是必须要考虑的重要内容,但是在进行实时编码时,可能要引起混淆。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 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时,该条件不起作用 <select id="xxx"> SELECT di.id, di.name, di.work_type, di.updated... <where> <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,添加如下 <property name="dynamic.classpath" value="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['font.sans-serif'] = ['SimHei'] # 能正确显示负号 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 -> 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("/hires") 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<String
使用vite构建项目报错 C:\Users\ychen\work>npm init @vitejs/app @vitejs/create-app is deprecated, use npm init vite instead C:\Users\ychen\AppData\Local\npm-