【问题标题】:Convert code to/from string将代码转换为字符串/从字符串转换
【发布时间】:2013-11-08 14:50:44
【问题描述】:

MIT Scheme 有string->input-port,Racket 有open-input-string。如何在纯 Scheme 中实现这一点(没有 Racket、Chicken、Gambit 或任何特定于实现的扩展)。

【问题讨论】:

  • 您的目标是哪个版本。现在的 R6RS 还是旧的 R5RS?

标签: scheme


【解决方案1】:

根据Chis' answer,我们有一个新的方案标准,R7RS。它提供了open-input-string

对于较旧的 R6RS,使用来自 (rnrs io ports (6)) 库的 make-custom-textual-input-port 实现相同功能很简单。这是我整理的:

#!r6rs

(import (rnrs base (6))
        (rnrs io ports (6))
        (rnrs mutable-strings (6))
        (rnrs io simple (6)))

(define (open-input-string str)
  ;; might not be so important to have a different indentifier
  ;; but this will make debugging easier if implementations use the 
  ;; id provided
  (define get-id
    (let ((n 0))
      (lambda (str)
        (set! n (+ n 1))
        (string->symbol 
         (string-append "string-port" str "-"
                        (number->string n))))))

  (let ((len (string-length str))
        (pos 0))
    (make-custom-textual-input-port 
     (get-id str)
     (lambda (string start count)
       (let loop ((cur-dst start)
                  (cur-src pos)
                  (n 0))
         (cond ((or (>= cur-src len)
                    (>= n count))
                (set! pos cur-src)
                n)
               (else
                (string-set! string cur-dst (string-ref str cur-src))
                (loop (+ cur-dst 1)
                      (+ cur-src 1)
                      (+ n 1))))))
     (lambda () pos)
     (lambda (new-pos) (set! pos new-pos))
     #f)))

(define test (open-input-string "(1 2 3 4)(5 6 7 8)"))
(define str (read test))  ; str == (1 2 3 4)
(define str2 (read test)) ; str2 == (5 6 7 8)

对于R5RS,除了使用文件之外没有其他方法可以做到这一点。

【讨论】:

  • 好吧,让我们做得更好:R7RS 最近获得批准,它内置了open-input-string。;-)
  • @ChrisJester-Young 我将在第二个 Ikarus 和 Racket 支持它时使用它:)
【解决方案2】:

在最近批准的R7RS中,直接提供open-input-string。 (感谢 Sylwester 提醒我要超越 R5RS。:-))


在 R5RS 中,字符串端口的纯 Scheme 实现并非易事,因为它需要您重新定义所有标准 I/O 函数。请参阅SRFI 6 以获取参考实现。

如果你的实现直接支持字符串端口,那就更好了。

【讨论】:

    【解决方案3】:

    将字符串写入(临时)文件,然后返回输入端口以将其读回。像这样:

    (define (open-input-string string)
      (let ((file "/tmp/foo"))
        (call-with-output-file file
          (lambda (port)
             (display string port)))
        (open-input-file file)))
    
    > (define ps (open-input-string "This is a test; it is only a test"))
    > ps
    #<input-port (textual) "/tmp/foo">
    > (read-line ps)
    "This is a test; it is only a test"
    

    请注意,您需要更熟练地使用file。例如,上面的代码只工作一次;它会在第二次调用时因“文件存在”而失败。但以上是对您问题的简单回答。

    【讨论】:

    • 写入固定的临时文件非常不安全,因为它为symlink attack 开辟了道路。为了安全起见,您必须使用mkstemp,这不是“纯方案”。
    • 谢谢。不是在这里解决世界的问题;只是为有需要的人回答问题。
    • 也许,但这仍然是一个糟糕的解决方案,类似于告诉某人使用gets(在 C 中)从标准输入中读取字符串。
    猜你喜欢
    • 2018-11-13
    • 1970-01-01
    • 1970-01-01
    • 2011-12-06
    • 1970-01-01
    • 2019-10-10
    • 2019-01-05
    • 1970-01-01
    相关资源
    最近更新 更多