11

我正在尝试使用 scheme 从文件中读取,并将其内容放入列表中。

问题是如何去掉问号、数字,只保留单词。我应该每次都使用循环检查吗?如果不是这样,我如何从“读取”中获取下一个单词的内容?

我尝试使用此代码解决它,但在获取文件末尾之前我找不到调用“读取”的方法;

(define Project
  (lambda (fileName)
    (if (null? fileName) 
        'error
        (readNext (open fileName) '()))))

(define readNext
  (lambda (fc tmp)
    (if (null? (read fc) "#<eof>")
        tmp
        (readNext fc (cons (read fc) tmp)))))
4

5 回答 5

14

最推荐的导入文本的方法是编辑文件并将其保存为定义变量的方案文件:

(define data "the text in
mydata.scm here")

然后调用:

(load "mydata.scm")

很多时候,并不是每个数据文件都可以被编辑并保存为方案文件,虽然换行符会自动转义,但双引号不能,这会在加载文件时产生问题。

一些实现特定的技术是:

;Chicken
(use utils)
(read-all "mydata.txt")

;Racket
(file->string "mydata.txt")

更便携的功能是:

;works in chicken-csi and Racket
(define (readlines filename)
  (call-with-input-file filename
    (lambda (p)
      (let loop ((line (read-line p))
                 (result '()))
        (if (eof-object? line)
            (reverse result)
            (loop (read-line p) (cons line result)))))))

由于读取行需要一个额外的文件,运行一个可执行编译的 chicken-csc 会出错。

读取文件最便携的方法是这个函数:

;works in Chicken, Racket, SISC
;Read a file to a list of chars
(define (file->char_list path)
 (call-with-input-file path
   (lambda (input-port)
     (let loop ((x (read-char input-port)))
       (cond 
        ((eof-object? x) '())
        (#t (begin (cons x (loop (read-char input-port))))))))))

此功能相当快速且可跨实现移植。所需要的只是将 char_list 转换为字符串。

最简单的方法是:

;may not work if there is limit on arguments
(apply string (file->char_list "mydata.txt"))

问题是某些实现对可以传递给函数的参数数量有限制。2049 个字符的列表在 Chicken 中不起作用。

另一种方法是:

;works in Chicken, Racket
(foldr (lambda (x y) (string-append (string x) y)) "" (file->char_list "mydata.txt"))

问题是:首先,foldr 没有被普遍认可(SISC),尽管它可以被定义。其次,由于附加了每个字符,这种方法非常慢。

我编写了接下来的两个函数将字符列表分割成嵌套列表,直到最低级别不会超过 Chicken 中的最大参数计数。第三个函数遍历嵌套的 char 列表并使用 string string-append 返回一个字符串:

(define (cleave_at n a)
  (cond
   ((null? a) '())
   ((zero? n) (list '() a))
   (#t 
    ((lambda (x)
      (cons (cons (car a) (car x)) (cdr x)))
     (cleave_at (- n 1) (cdr a))))))

(define (cleave_binary_nest n a)
 (cond
  ((equal? n (length a)) (list a))
  (#t 
   ((lambda (x)
     (cond
      ((> (length (car x)) n) (map (lambda (y) (cleave_binary_nest n y)) x))
      (#t x)))
    (cleave_at (floor (/ (length a) 2)) a)))))

(define (binary_nest_char->string a)
 (cond
  ((null? a) "")
  ((char? (car a)) (apply string a))
  (#t (string-append
    (binary_nest_char->string (car a)) (binary_nest_char->string (cdr a))))))

该函数是这样调用的:

;Works in Racket, Chicken, SISC
;faster than foldr method (3x faster interpreted Chicken) (30x faster compiled Chicken) (125x faster Racket gui)
(binary_nest_char->string (cleave_binary_nest 2048 (file->char_list "mydata.txt")))

为了减少字母字符和空格,还有两个函数:

(define (alphaspace? x)
 (cond
  ((and (char-ci>=? x #\a) (char-ci<=? x #\z)) #t)
  ((equal? x #\space) #t)
  (#t #f)))

(define (filter pred lis)
  ; if lis is empty
  (if (null? lis)
    ; return an empty list
    '()
    ; otherwise, if the predicate is true on the first element
    (if (pred (car lis))
      ; return the first element concatenated with the
      ; result of calling filter on the rest of lis
      (cons (car lis) (filter pred (cdr lis)))
      ; otherwise (if the predicate was false) just
      ; return the result of filtering the rest of lis
      (filter pred (cdr lis)))))

(define data (file->char_list "mydata.txt"))
(define data_alphaspace (filter alphaspace? data))
(define result (binary_nest_char->string (cleave_binary_nest 2048 data_alphaspace)))

这适用于 Racket、Chicken(解释和编译)和 SISC(Java)。这些方言中的每一个也应该适用于 Linux、Mac (OS X) 和 Windows。

于 2013-07-26T10:30:25.950 回答
5

也许这会让你开始。

(define (file->list-of-chars file)
  (with-input-from-file file
    (lambda ()
      (let reading ((chars '()))
        (let ((char (read-char)))
          (if (eof-object? char)
              (reverse chars)
              (reading (cons char chars))))))))
于 2013-05-02T14:03:28.800 回答
2

使用 SRFI-42 中的“list-ec”从文件中读取行:

(use srfi-42) ; Chicken
  or
(require srfi/42) ; Racket

(define (file->lines filename)
  (call-with-input-file filename
    (lambda (p)
      (list-ec (:port line p read-line) line))))

使用 SRFI-13 和 SRFI-14 解析一行:

(use srfi-13) (use srfi-14) ; Chicken
  or
(require srfi/13) (require srfi/14) ; Racket

(string-tokenize "hi; ho")
("hi;" "ho")

(string-tokenize "hi; ho" char-set:letter)
("hi" "ho")
于 2018-07-06T17:43:06.813 回答
1

我不知道像其他答案那样对可移植性说什么,但如果你要使用 Racket,它会像下面这样简单:

(file->lines "somefile")
于 2017-04-22T13:42:36.273 回答
1

更新:R7RS

在其新的 (2013) 标准 R7RS (PDF)下,Scheme 现在提供了函数read-string,该函数标准化并简化了对该线程问题的答案。让我们在一个名为的简单测试文件上演示它mydata.txt

bash$ cat mydata.txt
"Hello world!"
Is this microphone on?
Testing 1 2 3...

要将整个文件读入单个字符串,可以read-string在 Scheme REPL 上使用,如下所示:

> (read-string 100 (open-input-file "mydata.txt"))
"\"Hello world!\"\nIs this microphone on?\nTesting 1 2 3...\n"

当然,第二行是向您显示由 . 返回的字符串的 REPL 1read-string。请注意,引号已正确转义,这是 Will 的回答中解决的问题之一。

顺便说一句:第一个参数read-string表示它要读取的最大字节数。确保将其设置为反映您自己文件的实际大小的值,以免它们被截断。

可移植性

我用 Scheme 的 Chibi、Chicken 和 Gauche 实现验证了上述解决方案。至少在理论上,它也应该适用于所有其他符合 R7RS 的方案。网站 schemers.org维护着一个声称合规的实现表。显然,我不能保证他们的说法的准确性。

也可能很有趣

除了read-string,R7RS 标准及其实现还提供了一个read-bytevector函数,该函数在二进制文件上的工作方式相同。您可以使用它将二进制文件读入字节向量。

这里要提到的最后一个 R7RS 函数是read-line,它一次读取一个文本文件。因此,如果您想将文件读入行列表,例如 Python 的readlines函数,您现在可以实现readlines如下的 Scheme 版本:

(define (readlines file)
 (let ((infile (open-input-file file)))
   (let loop ((lines '())
              (next-line (read-line infile)))
    (if (eof-object? next-line)
        (begin (close-input-port infile) 
               (reverse lines))
        (loop (cons next-line  lines) 
              (read-line infile))))))

让我们在 REPL 中测试它:

> (define ls (readlines "mydata.txt"))
> (car ls)
"\"Hello world!\""
> (cadr ls)
"Is this microphone on?"
> (caddr ls)
"Testing 1 2 3..."

我希望这个更新有所帮助。

于 2020-08-23T14:31:32.967 回答