kar*_*sss -1 lisp list common-lisp
我有以下代码:
(defun read_coords (in)
(setq x (read-line))
(if (equalp x "0")
in
(progn
(append in (cons x nil))
(read_coords in)
)
)
)
(setq coords (read_coords (cons nil nil)))
Run Code Online (Sandbox Code Playgroud)
目标是读取输入行并将它们存储在列表中.问题是列表coords
保持不变(因此仅包含NIL
).我究竟做错了什么?
这是您的代码,具有更好的格式:
(defun read_coords (in)
(setq x (read-line))
(if (equalp x "0")
in
(progn
(append in (cons x nil))
(read_coords in))))
(setq coords (read_coords (cons nil nil)))
Run Code Online (Sandbox Code Playgroud)
现在,回报价值是read_coords
多少?函数包含隐式PROGN
,因此它是最后一种形式.这里,最后一个表格是IF
.根据测试结果,返回值是或者在else位置in
的返回值.因此,测试失败时的返回值是通过调用获得的值.当递归最终没有错误结束时,它必然在返回的then分支中.请注意,在整个执行过程中永远不会修改.PROGN
(read_coords in)
IF
in
in
实际上,APPEND
根据其输入创建了一个新的列表.换句话说,新列表是调用返回的值APPEND
,遗憾的是,它永远不会存储在任何地方.计算完成没有任何副作用,其结果被丢弃.您可能应该这样做:
(read_coords (append in (cons x nil)))
Run Code Online (Sandbox Code Playgroud)
因此,新列表作为参数传递给递归调用.
(setq x (read-line))
Run Code Online (Sandbox Code Playgroud)
不要SETQ
用于定义局部变量.你需要在LET
这里使用-binding.否则,您将以与实现相关的方式更改全局词法范围(除非您定义了一个名为的全局变量x
,这是不好的,因为它违反了特殊变量的命名约定,应该具有这个特性*earmuffs*
).在函数内部变换全局变量使其可能不可重入,这非常糟糕.
(cons x nil)
Run Code Online (Sandbox Code Playgroud)
要使用一个元素构建列表,只需使用LIST
:
(list x)
Run Code Online (Sandbox Code Playgroud)
最后,请注意,您不应在名称中使用下划线,而是选择短划线.因此,您的功能应该被命名read-coords
,或者更好read-coordinates
.
(equalp x "0")
Run Code Online (Sandbox Code Playgroud)
尽管上面的说法是正确的,但最好在string=
这里使用,因为你知道READ-LINE
返回一个字符串.
您正在反复添加列表,这会为正在读取的每个元素创建一个副本.对于可以使用线性算法完成的任务,您的代码在时间和空间使用方面是二次的.此外,您正在使用尾递归函数,其中简单的迭代将更清晰,更惯用.我们通常不使用Common Lisp中的尾递归过程,因为该语言提供了迭代控制结构,并且因为不能保证始终应用尾部合并优化(优化不是强制性的(这是一件好事)并不妨碍实现提供它,即使它可能需要用户的其他声明).更好地使用LOOP
这里:
(defun read-coordinates (&optional (input-stream *standard-input*))
(loop for line = (read-line input-stream nil nil)
until (or (not line) (string= line "0"))
collect line))
Run Code Online (Sandbox Code Playgroud)
我们在参数中传递输入流,默认为*STANDARD-INPUT*
.然后READ-LINE
在忽略错误的同时读取行(感谢第一个NIL).如果发现错误,例如当我们到达文件结尾时,返回的值是NIL(由于第二个NIL).LOOP
读取行的结尾为NIL或等于"0"
.循环将所有连续的行累积到列表中.
归档时间: |
|
查看次数: |
948 次 |
最近记录: |