在multiple-value-bind中使用values-form中的变量

时间:2017-06-04 15:55:36

标签: common-lisp

我正在尝试在lisp中实现一个函数read-from-whole-string,它使用内置函数read-from-string

关于read-from-string

read-from-string用于从字符串中读取对象,可以使用关键字:start:end等指定对象。它返回两个值,即对象和下一个的位置元素,没有读过。我计划使用这个值。

计划

计划是使用从read-from-string函数返回的第二个值,再次使用它来插入关键字:start的值。但问题是函数multiple-value-bind。我们不能:

  1. multiple-value-bind中初始化变量。
  2. 使用刚刚定义到values-form的变量。
  3. 具体来说,我想:

    (multiple-value-bind (x (y 0))
             (read-from-string str :Start y)
             y)
    

    赞赏任何其他干净的方法。

3 个答案:

答案 0 :(得分:3)

CL-USER 118 > (let ((string "10 2 300 foo \"bar\""))
                (loop with pos and value
                      for start = 0 then pos
                      do (setf (values value pos)
                               (read-from-string string nil string :start start))
                      while (not (eq value string))
                      collect value))
(10 2 300 FOO "bar")

CL-USER 120 > (let ((string "10 2 300 foo \"bar\""))
                (loop for start = 0 then pos
                      for (value pos) = (multiple-value-list
                                         (read-from-string string nil string
                                                           :start start))
                      while (not (eq value string))
                      collect value))
(10 2 300 FOO "bar")

答案 1 :(得分:2)

你可以创建一个字符串流,读取并丢弃一个对象,然后再读一遍:

$ clisp -q
[1]> (with-input-from-string (*standard-input* "1 2") (read) (read))
2

要读取输入字符串中的所有元素,我们可以在read上循环并使用唯一值来检测EOF,而不是让它抛出错误:

(with-input-from-string (s "1 2 3 4")
  (loop with eof = '#:eof
        for item = (read s nil eof)
        until (eq item eof)
        collecting item))
-> (1 2 3 4)

要提取字符串中的所有对象,我们还可以将其包装在括号中并使用read-from-string的单个应用程序:

(read-from-string (format nil "(~a)" "1 2 3 4"))
-> (1 2 3 4); 11

当字符串包含此类问题时,存在缺乏诊断的危险:"a b ) c d"。我们可以通过将返回的长度与字符串长度进行比较来检查这种情况,以确保所有内容都被读取,如果不是,则发出一些通用诊断。

(defun read-whole-string (str)
  (let ((par-str (format nil "(~a)" str)))
    (multiple-value-bind (obj len) (read-from-string par-str)
      (if (eql len (length par-str))
        obj
        (error "read-whole-string: ~a is malformed at position ~s"
               str (- len 2))))))

答案 2 :(得分:0)

实现函数read-from-whole-string的其他方法可能不太干净:

(defun read-from-whole-string (str)
  (loop 
    collect (multiple-value-bind (x y)
                (read-from-string str)
              (setf str (delete-if #'atom str :start 0 :end (if (= (length str) y)
                                                                y
                                                               (1- y))))
              x)
    until (zerop (length str))
        ))