与球拍中的流进行模式匹配?
Pattern matching with streams in Racket?
模式匹配是 let 子句中手动解构的一种非常可读的替代方法。我想知道是否可以将它与流一起使用,就像我们可以与列表一样。
作为示例,考虑一个简单的 add-between
实现来生成一个序列,其中在原始序列的每个元素之间插入一个元素。
(define (add-between lst sep)
(match lst
['() '()]
[(cons v vs)
(cons v
(cons sep
(add-between vs sep)))]))
用法:
(add-between (list 'a 'b 'c) 'and) ; => '(a and b and c and)
如果我们想对任意序列(如流)做同样的事情怎么办?
(define (add-between seq sep)
(match seq
['() '()]
[(cons v vs)
(stream-cons v
(stream-cons sep
(add-between vs sep)))]))
这会产生错误:
(add-between (stream 'a 'b 'c) 'and)
; match: no matching clause for #<stream>
; stdin:1:1
; Context:
; /Applications/Racket v7.5/collects/racket/match/runtime.rkt:24:0 match:error
; /Applications/Racket v7.5/collects/racket/repl.rkt:11:26
如果我们能够在通用序列类型上进行模式匹配,那将是理想的,因为这将封装列表和流。
您可以定义自己的流模式匹配形式,使用 define-match-expander
、?
和谓词,以及 app
和访问器。
#lang racket
(require syntax/parse/define (prefix-in * racket/stream))
(define (stream-empty? v) (and (stream? v) (*stream-empty? v)))
(define (stream-cons? v) (and (stream? v) (not (*stream-empty? v))))
(define-match-expander stream-cons
(syntax-parser
[(_ fst rst) #'(? stream-cons? (app stream-first fst) (app stream-rest rst))])
(syntax-parser
[(_ fst rst) #'(*stream-cons fst rst)]))
(define-match-expander stream*
(syntax-parser
[(_ rst) #'rst]
[(_ fst more ... rst) #'(stream-cons fst (stream* more ... rst))])
(syntax-parser
[(_ init ... rst) #'(*stream* init ... rst)]))
(define-match-expander stream
(syntax-parser
[(_) #'(? stream-empty?)]
[(_ elem ...) #'(stream* elem ... (? stream-empty?))])
(syntax-parser
[(_ elem ...) #'(*stream elem ...)]))
使用它:
> (define (add-between seq sep)
(match seq
[(stream) (stream)]
[(stream-cons v vs)
(stream-cons v
(stream-cons sep
(add-between vs sep)))]))
> (add-between (stream 'a 'b 'c) 'and)
#<stream>
> (stream->list (add-between (stream 'a 'b 'c) 'and))
'(a and b and c and)
@AlexKnauth's is illuminating. I've since also discovered that the data/collection
library provides a built-in way to do this for arbitrary sequences (that is, sequences as defined here)。使用此匹配模式,问题中的代码可以针对通用序列编写为:
(require data/collection)
(define (add-between seq sep)
(match seq
[(sequence) empty-stream]
[(sequence v vs ...)
(stream-cons v
(stream-cons sep
(add-between vs sep)))]))
使用它:
(sequence->list (add-between (stream 'a 'b 'c) 'and))
=> '(a and b and c and)
(sequence->string (add-between "hello" #\space))
"h e l l o "
模式匹配是 let 子句中手动解构的一种非常可读的替代方法。我想知道是否可以将它与流一起使用,就像我们可以与列表一样。
作为示例,考虑一个简单的 add-between
实现来生成一个序列,其中在原始序列的每个元素之间插入一个元素。
(define (add-between lst sep)
(match lst
['() '()]
[(cons v vs)
(cons v
(cons sep
(add-between vs sep)))]))
用法:
(add-between (list 'a 'b 'c) 'and) ; => '(a and b and c and)
如果我们想对任意序列(如流)做同样的事情怎么办?
(define (add-between seq sep)
(match seq
['() '()]
[(cons v vs)
(stream-cons v
(stream-cons sep
(add-between vs sep)))]))
这会产生错误:
(add-between (stream 'a 'b 'c) 'and)
; match: no matching clause for #<stream>
; stdin:1:1
; Context:
; /Applications/Racket v7.5/collects/racket/match/runtime.rkt:24:0 match:error
; /Applications/Racket v7.5/collects/racket/repl.rkt:11:26
如果我们能够在通用序列类型上进行模式匹配,那将是理想的,因为这将封装列表和流。
您可以定义自己的流模式匹配形式,使用 define-match-expander
、?
和谓词,以及 app
和访问器。
#lang racket
(require syntax/parse/define (prefix-in * racket/stream))
(define (stream-empty? v) (and (stream? v) (*stream-empty? v)))
(define (stream-cons? v) (and (stream? v) (not (*stream-empty? v))))
(define-match-expander stream-cons
(syntax-parser
[(_ fst rst) #'(? stream-cons? (app stream-first fst) (app stream-rest rst))])
(syntax-parser
[(_ fst rst) #'(*stream-cons fst rst)]))
(define-match-expander stream*
(syntax-parser
[(_ rst) #'rst]
[(_ fst more ... rst) #'(stream-cons fst (stream* more ... rst))])
(syntax-parser
[(_ init ... rst) #'(*stream* init ... rst)]))
(define-match-expander stream
(syntax-parser
[(_) #'(? stream-empty?)]
[(_ elem ...) #'(stream* elem ... (? stream-empty?))])
(syntax-parser
[(_ elem ...) #'(*stream elem ...)]))
使用它:
> (define (add-between seq sep)
(match seq
[(stream) (stream)]
[(stream-cons v vs)
(stream-cons v
(stream-cons sep
(add-between vs sep)))]))
> (add-between (stream 'a 'b 'c) 'and)
#<stream>
> (stream->list (add-between (stream 'a 'b 'c) 'and))
'(a and b and c and)
@AlexKnauth's data/collection
library provides a built-in way to do this for arbitrary sequences (that is, sequences as defined here)。使用此匹配模式,问题中的代码可以针对通用序列编写为:
(require data/collection)
(define (add-between seq sep)
(match seq
[(sequence) empty-stream]
[(sequence v vs ...)
(stream-cons v
(stream-cons sep
(add-between vs sep)))]))
使用它:
(sequence->list (add-between (stream 'a 'b 'c) 'and))
=> '(a and b and c and)
(sequence->string (add-between "hello" #\space))
"h e l l o "