延迟读取球拍中的自定义类型

Lazy reads of custom types in Racket

我是 Racket 的新手,我正在尝试编写一个函数来读取文件的行,将每一行解析为 struct,return 我的惰性序列数据类型。这是我的输入格式的一个简单示例(具有行名和列名的矩阵)。我的实际输入格式还包括一个 header 行,我在这里省略它,并且包含非常大的文件,这就是我需要懒惰的原因。

R1 1.0 2.3 1.2 R2 1.2 3.1 3.4

这是我最近的尝试:

(struct row (key data))

(define (read-matrix in)
  (for [(line (in-lines in))]
    (let ([fields (string-split line "\t")]
      (row (first fields) (list->vector (map string->number (rest fields))))
      )))

我还尝试了许多其他方法,包括使用 call-with-input-file。我对上述方法的问题是,如果我使用 #lang racket 它不是懒惰的,并且 #lang lazy string-split 未定义。我应该补充一点,在我的用例中,我想要的语义是在使用完整个序列后关闭端口,因为我可以保证要么使用整个序列,要么程序终止。

那么,我走在正确的轨道上吗?我应该采取什么方法来解决这个问题?谢谢!

尝试使用 SRFI-13 中的函数,这是一个字符串处理库,在 #lang lazy 中也可用:

(require srfi/13)

然后这样做:

[fields (string-tokenize line)]

最后我发现答案就是用Racket的sequence、streams和generator库来做这种东西。生成器特别好,允许一个简单的 Python-like "yield" 函数。这些功能允许惰性序列,而无需 #lang lazy.

提供的全面惰性评估

http://docs.racket-lang.org/reference/streams.html

我正在离线撰写这个答案,回来后发现您已经回答了大部分问题。如果细节对任何人有帮助,我都会 post。


如果你真的需要#lang lazy,又想用string-split,我想你可以直接(require racket/string)来用吗?

我不确定我是否完全理解 "lazy" 在这里的意思。如果您担心的话,使用 in-lines 不会将整个文件吸入内存。它将一次处理一行。

你可以做的一件事是定义一个辅助函数,它处理读取和解析行,检查 eof,并自动关闭输入端口:

(struct row (key data)
        #:transparent)

;; Example couple lines of input to use below.
(define text "R1 1.0 2.3 1.2\nR2 1.2 3.1 3.4")

;; read-matrix-row : input-port? -> (or/c eof row?)
;;
;; Given an input port, try to read another row.
(define (read-matrix-row in)
  (match (read-line in)
    [(? eof-object?)
     (close-input-port in)
     eof]
    [line (match (string-split line " ")
            [(cons key data)
             (row key (list->vector (map string->number data)))])]))

您可以通过多种方式使用此功能。一种方法是 in-producer:

;; Example use with in-producer:
(let ([in (open-input-string text)])
  (for/list ([x (in-producer read-matrix-row eof in)])
    x))
;; => (list (row "R1" '#(1.0 2.3 1.2))
;;          (row "R2" '#(1.2 3.1 3.4)))

该示例使用 for/list 生成 list。当然,如果你有一个巨大的输入文件,那将产生一个巨大的 list。但是您可以将它们一一显示,或者将它们一一写入文件或数据库:

;; Example use, displaying one by one.
(let ([in (open-input-string text)])
  (for ([x (in-producer read-matrix-row eof in)])
    (displayln x))) ;or write to some file, for example

如果您更喜欢 stream 界面,则可以很容易地从任何序列(包括“in-producer”)创建流:

;; If you prefer a stream interface, we can use sequence->stream to
;; transform the producer sequence into a stream:
(define (matrix-row-stream in)
  (sequence->stream (in-producer read-matrix-row eof in)))

;; Example interactive use of the stream
(define stm (matrix-row-stream (open-input-string text)))
(stream-empty? stm)                     ;#f
(stream-first stm)                      ;(row "R1" '#(1.0 2.3 1.2))
(stream-empty? (stream-rest stm))       ;#f
(stream-first (stream-rest stm))        ;(row "R2" '#(1.2 3.1 3.4))
(stream-empty? (stream-rest (stream-rest stm))) ;#t