将Scheme表达式转换为字符串

6

给定一个表达式 '(lambda (x) x),我该如何将其转换为字符串。我认为symbol->string可以完成这个任务,但它不能转换符号。

例如,对于一个名为to-string的宏:

(to-string (lambda(x) x)),这应该返回 >> "(lambda (x) x)"

有什么想法吗?谢谢。


1
只是澄清一下,我正在处理引用列表。我会遍历列表并进行字符串连接,但我不知道如何处理可变参数,例如'(lambda (x . y) (display x)(display y))。 - user200654
你使用的是哪个版本的Scheme?它是R6RS兼容的,还是只有R5RS兼容的? - Alex Knauth
6个回答

8

标准方案(至少在R5RS意义上)无法做到这一点,因此如果您希望代码具有可移植性,则需要自己遍历结构。 繁琐但不太复杂(即使对于带点列表也是如此)。 但是,如果您只想要一些工作版本,那么您应该查看实现手册并搜索如何执行此操作的方法。 答案通常很简单,例如,在PLT Scheme中,您将使用(format "~s" '(lambda ...))


2
那个答案是正确的,不过如果实现支持 SRFI 6,你可以只需创建一个输出字符串端口,并对其进行 write 操作。 - C. K. Young
1
重点是直接这样做在某些情况下可能更有效。如果你走的是srfi路线,那么srfi-28会正式规范format。(是的,参考实现使用srfi-6,但实现通常会使用任何内置功能——mzscheme实现srfi-26只使用了自己的“format”。) - Eli Barzilay

5

'(lambda (x) x)这个表达式是一个带引号的列表。

(lambda (x) x)这个表达式是一种编译后的不透明可执行内部对象。

symbol->string函数只是将一个符号转换为一个字符串,而这个字符串是由一系列字符组成的。

如果你正在处理一个列表,那么你可以简单地遍历该列表并打印出各个组件。实际上,(write '(lambda (x) x))只会简单地打印该列表。

许多Scheme方言都有类似于(with-output-to-string ... )的机制,用于返回所有写入标准端口的输出字符串。

然而,如果你执行(write (lambda (x) x)),你将得到谁知道什么样的结果。你会得到实现在转储可执行函数类型时所提供的任何内容。有些可能会打印出显示源代码的"反汇编"。其他人则可能只是打印出#function或类似无用的东西。

总之,如果你只想打印一个列表,那么有各种机制可以做到这一点。

如果你想打印已编译函数的源代码,则需要解决完全不同的问题,这与具体实现有关,可能是不可能的。


1
如果您正在使用兼容R6RS的Scheme,您可以使用write函数与call-with-string-output-port相结合。
#!r6rs
(import (rnrs base)
        (rnrs io ports)   ;for call-with-string-output-port
        (rnrs io simple)) ;for write

;; Produces a string representation of the S-Expression e
(define (expr->string e)
  (call-with-string-output-port
   (lambda (out) (write e out))))

一些例子:

> (expr->string '(lambda (x) (+ x 1)))
"(lambda (x) (+ x 1))"
> (expr->string '(lambda (x) (string-append x " and cheese!")))
"(lambda (x) (string-append x \" and cheese!\"))"

顺便提一下,通常也可以使用{{link1:read}}与{{link2:open-string-input-port}}相结合,以相反的方向进行操作。
;; Produces the S-Expression represented by the string.
;; In most cases this will be a left-inverse of expr->string,
;; so that (string->expr (expr->string e)) = e.
;; If the string has multiple S-Expressions represented
;; in it, this only returns the first one.
(define (string->expr s)
  (read (open-string-input-port s)))

一些例子:

> (string->expr "(lambda (x) (+ x 1))")
(lambda (x) (+ x 1))
> (equal? (string->expr "(lambda (x) (+ x 1))")
          '(lambda (x) (+ x 1)))
#t
> (equal? (string->expr (expr->string '(lambda (x) (+ x 1))))
          '(lambda (x) (+ x 1)))
#t

1

很不幸,我正在使用的Scheme实现没有实现pretty-format :( 有其他替代方案吗? - user200654

1
你应该遍历cons链表。当一个新的cons开始时写"(",当它结束时写")",并使用symbol->string将cons中的符号转化为字符串。
你可以通过类型分派来扩展此功能。也许在Scheme中也有漂亮打印(pretty print)的功能?

感谢 Trickster 的快速回复。我也考虑过这个解决方案,但是我不知道如何处理可变参数函数的情况,例如 '(lambda (x . y) (+ x y)),该如何确定 x 和 y 之间的点并输出“(lambda (x . y) (+ x y))”。 - user200654
好的,(x y) 是 (cons x (cons y nil)),而 (x . y) 是 (cons x y)。 - Ilya Khaprov

0
这是一个简单的代码,我认为它可以实现你想要的功能。
它不使用任何特定于实现的内容,非常直观。
但请注意,它无法正确处理字符串中的特殊字符(例如,“a\"b\"c”将变成“a"b"c")。
(define join
    (lambda (l delim to-str)
      (fold-left
        (lambda (str elem)
          (string-append str delim (to-str elem)))
        (to-str (car l))
        (cdr l))))
(define sexpr->string 
      (lambda (sexpr)
        (cond 
          ((number? sexpr) (number->string sexpr))
          ((symbol? sexpr) (symbol->string sexpr))
          ((boolean? sexpr) (if sexpr "#t" "#f"))
          ((string? sexpr) (string-append "\"" sexpr "\""))
          ((char? sexpr) (string-append "#\\" (string sexpr)))
          ((vector? sexpr) 
           (let ((s-vec (join (vector->list sexpr) " " sexpr->string)))
             (string-append "#(" s-vec ")")))
          ((null? sexpr) "()")
          ((list? sexpr) (string-append "(" (join sexpr " " sexpr->string) ")"))

          ((pair? sexpr) 
           (let ((s-car (sexpr->string (car sexpr)))
                 (s-cdr (sexpr->string (cdr sexpr))))
             (string-append "(" s-car " . " s-cdr ")"))))))

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接