스트림
스트림 (Streams)
스트림은 stream-first와 stream-rest를 통해 함수형 반복을 지원하는 일종의 시퀀스입니다. stream-cons 폼이 지연(lazy) 스트림을 만들고, 일반 리스트도 스트림으로 쓰일 수 있으며, in-range나 in-naturals 같은 함수도 스트림을 만듭니다.
출처: Racket Reference
본문
스트림은 stream-first와 stream-rest를 통해 함수형 반복을 지원하는 일종의 시퀀스입니다. stream-cons 폼이 지연 스트림을 만들지만, 일반 리스트도 스트림으로 사용할 수 있고, in-range, in-naturals 같은 함수도 스트림을 만듭니다.
(require racket/stream) ; package: base
이 섹션에 문서화된 바인딩은 racket/stream과 racket 라이브러리가 제공하지만, racket/base는 제공하지 않습니다.
procedure
(stream? v) → boolean?
v : any/c
v를 스트림으로 사용할 수 있으면 #t를, 그렇지 않으면 #f를 반환합니다.
procedure
(stream-empty? s) → boolean?
s : stream?
s에 요소가 없으면 #t를, 그렇지 않으면 #f를 반환합니다.
procedure
(stream-first s) → any
s : (and/c stream? (not/c stream-empty?))
s의 첫 번째 요소의 값(들)을 반환합니다.
procedure
(stream-rest s) → stream?
s : (and/c stream? (not/c stream-empty?))
첫 번째 요소가 없는 s와 동등한 스트림을 반환합니다.
syntax
(stream-cons first-expr rest-expr)
(stream-cons #:eager first-expr rest-expr)
(stream-cons first-expr #:eager rest-expr)
(stream-cons #:eager first-expr #:eager rest-expr)
첫 번째 요소가 first-expr로 결정되고 나머지가 rest-expr로 결정되는 스트림을 만들어냅니다.
first-expr 앞에 #:eager가 없으면 first-expr은 즉시 평가되지 않습니다. 대신 결과 스트림에 대한 stream-first가 first-expr의 평가를 (한 번) 강제해서 스트림의 첫 번째 요소를 만들어냅니다. first-expr을 평가하다가 예외가 발생하거나 자기 자신을 강제하려 하면 exn:fail:contract 예외가 발생하며, 이후의 강제 평가 시도는 또 다른 예외를 촉발합니다.
rest-expr 앞에 #:eager가 없으면 rest-expr은 즉시 평가되지 않습니다. 대신 결과 스트림에 대한 stream-rest는 (stream-lazy rest-expr)이 만들어내는 것과 같은 또 다른 스트림을 만들어냅니다.
first-expr이 만들어내는 스트림의 첫 번째 요소는 여러 값(multiple values)일 수 있습니다. rest-expr은 평가될 때 스트림을 만들어내야 하며, 그렇지 않으면 exn:fail:contract? 예외가 발생합니다.
base 패키지의 8.0.0.12 버전에서 변경됨: #:eager 옵션 추가. 8.8.0.7 버전에서 변경됨: 여러 값을 허용하도록 변경됨.
syntax
(stream-lazy stream-expr)
(stream-lazy #:who who-expr stream-expr)
(delay stream-expr)과 비슷하지만, 결과가 프로미스(promise) 대신 스트림이며, stream-expr은 결국 강제될 때 스트림을 만들어내야 합니다. stream-lazy가 만들어내는 스트림은 stream-expr이 만들어내는 스트림과 같은 내용을 갖습니다. 즉 결과 스트림에 대한 stream-first 같은 연산은 stream-expr을 강제하고 그 결과에 대해 다시 시도합니다.
stream-expr을 평가하다가 예외가 발생하거나 자기 자신을 강제하려 하면 exn:fail:contract 예외가 발생하며, 이후의 강제 평가 시도는 또 다른 예외를 촉발합니다.
who-expr이 제공되면, 지연 스트림을 구성할 때 평가됩니다. stream-expr이 나중에 스트림이 아닌 값을 만들어내고, who-expr이 기호 값을 만들어냈다면, 그 기호가 오류 메시지에 사용됩니다.
base 패키지의 8.0.0.12 버전에서 추가되었습니다.
procedure
(stream-force s) → stream?
s : stream?
stream-lazy의 지연 스트림, stream-cons의 stream-rest 등에서 온 지연 스트림의 평가를 강제하고, 강제된 스트림을 반환합니다. s가 지연 스트림이 아니면 s가 반환됩니다.
보통 stream-force는 필요 없습니다. stream-first, stream-rest, stream-empty? 같은 연산이 필요에 따라 지연 스트림을 강제하기 때문입니다. 드물게 stream-force가 스트림의 밑바탕 구현을 드러내는 데 유용할 수 있습니다(예: prop:stream 속성을 가진 구조체 타입의 인스턴스인 스트림).
base 패키지의 8.0.0.12 버전에서 추가되었습니다.
syntax
(stream elem-expr ...)
elem-expr = (values single-expr ...)
| single-expr
empty-stream으로 끝나는 중첩된 stream-cons들의 약식 표기입니다. 매치 패턴으로서, stream은 elem-expr 개수만큼의 요소를 가진 스트림에 매치되며, 각 요소는 대응하는 elem-expr 패턴에 매치되어야 합니다. 패턴 elem-expr은 (values single-expr ...)일 수 있는데, 이것은 스트림에서 여러 값을 가진 요소에 매치됩니다.
base 패키지의 8.8.0.7 버전에서 변경됨: 여러 값을 허용하도록 변경됨.
syntax
(stream* elem-expr ... tail-expr)
중첩된 stream-cons들의 약식 표기이지만, tail-expr은 강제될 때 스트림을 만들어내야 하며, 그 스트림은 empty-stream 대신 스트림의 나머지로 사용됩니다. 리스트에 대한 list*와 비슷합니다. 매치 패턴으로서 stream*은 stream 패턴과 비슷하지만, tail-expr 패턴은 마지막 elem-expr 다음의 스트림 "나머지"에 매치됩니다.
base 패키지의 6.3 버전에서 추가됨. 8.0.0.12 버전에서 변경됨: elem-expr이 0개 제공되어도 tail-expr을 지연시키도록 변경됨. 8.8.0.7 버전에서 변경됨: 여러 값을 허용하도록 변경됨.
procedure
(in-stream s) → sequence?
s : stream?
s와 동등한 시퀀스를 반환합니다.
in-stream 호출은 for 절에 직접 나타나면 스트림 반복에 대해 더 나은 성능을 제공할 수 있습니다.
반복 중 스트림 요소의 도달 가능성(reachability)에 대한 정보는 for를 참고하세요.
base 패키지의 6.7.0.4 버전에서 변경됨: for에서 스트림의 요소 도달 가능성 보장을 개선함.
value
empty-stream : stream?
요소가 없는 스트림입니다.
procedure
(stream->list s) → list?
s : stream?
요소들이 s의 요소인 리스트를 반환합니다. 각 요소는 단일 값이어야 합니다. s가 무한하면 이 함수는 종료하지 않습니다.
procedure
(stream-length s) → exact-nonnegative-integer?
s : stream?
s의 요소 수를 반환합니다. s가 무한하면 이 함수는 종료하지 않습니다.
지연 스트림의 경우, 이 함수는 하위 스트림들만 강제하고 스트림의 요소는 강제하지 않습니다.
procedure
(stream-ref s i) → any
s : stream?
i : exact-nonnegative-integer?
s의 i번째 요소를 반환합니다(여러 값일 수 있음).
procedure
(stream-tail s i) → stream?
s : stream?
i : exact-nonnegative-integer?
첫 i개 요소가 생략된 s와 동등한 스트림을 반환합니다.
s에서 요소를 추출하는 것이 부수 효과와 연관되어 있다면, 결과 스트림에서 첫 번째 요소가 추출될 때까지는 추출되지 않습니다.
procedure
(stream-take s i) → stream?
s : stream?
i : exact-nonnegative-integer?
s의 첫 i개 요소로 이루어진 스트림을 반환합니다.
procedure
(stream-append s ...) → stream?
s : stream?
각 스트림의 모든 요소를 원래 스트림들에서 나타나는 순서대로 포함하는 스트림을 반환합니다. 새 스트림은 지연적으로 구성되며, 마지막으로 주어진 스트림이 결과의 꼬리(tail)에 사용됩니다.
procedure
(stream-map f s) → stream?
f : procedure?
s : stream?
s의 각 요소에 f를 적용한 결과를 포함하는 스트림을 반환합니다. 새 스트림은 지연적으로 구성됩니다.
procedure
(stream-andmap f s) → boolean?
f : (-> any/c ... boolean?)
s : stream?
s의 모든 요소에 대해 f가 참 결과를 반환하면 #t를 반환합니다. s가 무한하고 f가 결코 거짓 결과를 반환하지 않으면 이 함수는 종료하지 않습니다.
procedure
(stream-ormap f s) → boolean?
f : (-> any/c ... boolean?)
s : stream?
s의 어떤 요소에 대해 f가 참 결과를 반환하면 #t를 반환합니다. s가 무한하고 f가 결코 참 결과를 반환하지 않으면 이 함수는 종료하지 않습니다.
procedure
(stream-for-each f s) → void?
f : (-> any/c ... any)
s : stream?
s의 각 요소에 f를 적용합니다. s가 무한하면 이 함수는 종료하지 않습니다.
procedure
(stream-fold f i s) → any/c
f : (-> any/c any/c ... any/c)
i : any/c
s : stream?
s의 각 요소에 대해 i를 초기 누산기로 하여 f를 접습니다(fold). s가 무한하면 이 함수는 종료하지 않습니다. f 함수는 누산기를 첫 번째 인자로, 다음 스트림 요소를 두 번째 인자로 받습니다.
procedure
(stream-count f s) → exact-nonnegative-integer?
f : procedure?
s : stream?
s에서 f가 참 결과를 반환하는 요소의 수를 반환합니다. s가 무한하면 이 함수는 종료하지 않습니다.
procedure
(stream-filter f s) → stream?
f : (-> any/c ... boolean?)
s : stream?
s에서 f가 참 결과를 반환하는 요소들로 이루어진 스트림을 반환합니다. 새 스트림은 지연적으로 구성되지만, s에 f가 거짓 결과를 반환하는 요소가 무한히 많으면, 이 스트림에 대한 연산은 무한 하위 스트림 구간에서 종료하지 않습니다.
procedure
(stream-add-between s e) → stream?
s : stream?
e : any/c
s의 요소들로 이루어지되 s의 각 요소 쌍 사이에 e가 들어가는 스트림을 반환합니다. 새 스트림은 지연적으로 구성됩니다.
syntax
(for/stream (for-clause ...) body-or-break ... body)
syntax
(for*/stream (for-clause ...) body-or-break ... body)
각각 for/list과 for*/list처럼 반복하지만, 결과는 리스트 대신 스트림으로 지연적으로 모입니다.
대부분의 for 폼과 달리, 이 폼들은 지연적으로 평가되므로, 결과 스트림이 강제될 때까지 각 body는 평가되지 않습니다. 이로 인해 for/stream과 for*/stream은 유한한 상대편들과 달리 무한 시퀀스를 반복할 수 있습니다.
예시:
> (for/stream ([i '(1 2 3)]) (* i i))
#<stream>
> (stream->list (for/stream ([i '(1 2 3)]) (* i i)))
'(1 4 9)
> (stream-ref (for/stream ([i '(1 2 3)]) (displayln i) (* i i)) 1)
2
4
> (stream-ref (for/stream ([i (in-naturals)]) (* i i)) 25)
625
> (stream-ref (for/stream ([i (in-naturals)]) (values i (add1 i))) 10)
10
11
base 패키지의 6.3.0.9 버전에서 추가됨. 8.8.0.7 버전에서 변경됨: 여러 값을 허용하도록 변경됨.
value
gen:stream : any/c
구조체 타입에 세 개의 메서드를 연관시켜 스트림에 대한 제네릭 인터페이스(Generic Interfaces 참고)를 구현합니다.
메서드 구현을 공급하려면 구조체 타입 정의에서 #:methods 키워드를 사용해야 합니다. 다음 세 메서드를 구현해야 합니다:
stream-empty?: 인자 하나를 받음stream-first: 인자 하나를 받음stream-rest: 인자 하나를 받음
예시:
> (struct list-stream (v)
#:methods gen:stream
[(define (stream-empty? stream)
(empty? (list-stream-v stream)))
(define (stream-first stream)
(first (list-stream-v stream)))
(define (stream-rest stream)
(list-stream (rest (list-stream-v stream))))])
> (define l1 (list-stream '(1 2)))
> (stream? l1)
#t
> (stream-first l1)
1
base 패키지의 8.7.0.5 버전에서 변경됨: stream-empty?, stream-first, stream-rest 중 하나라도 생략하면 이제 문법 오류가 되도록 검사를 추가함.
value
prop:stream : struct-type-property?
스트림 API에 대한 사용자 정의 확장을 정의하는 데 사용하는 구조체 타입 속성입니다. prop:stream 속성 사용은 권장되지 않습니다. 대신 gen:stream 제네릭 인터페이스를 사용하세요. gen:stream의 메서드들과 같은 인자를 받는 세 개의 절차로 이루어진 벡터를 받습니다.
procedure
(stream/c c) → contract?
c : contract?
스트림을 인식하는 계약(contract)을 반환합니다. 스트림의 모든 요소가 c와 매치되어야 합니다.
c 인자가 플랫 계약(flat contract) 또는 채퍼론 계약(chaperone contract)이면, 결과는 채퍼론 계약이 됩니다. 그렇지 않으면 결과는 임퍼서네이터 계약(impersonator contract)이 됩니다.
stream/c 계약이 스트림에 적용되면, 결과는 입력과 eq?가 아닙니다. 결과는 계약 유형에 따라 입력의 채퍼론 또는 임퍼서네이터가 됩니다.
스트림에 대한 계약은 필요에 따라 지연적으로 평가됩니다(스트림이 무한할 수 있기 때문). 계약 위반은 위반된 값이 스트림에서 검색될 때까지 발생하지 않습니다. 이 규칙의 예외로, 리스트인 스트림은 c가 listof와 함께 사용된 것처럼 즉시 검사됩니다.
계약이 스트림에 적용되고, 그 스트림이 나중에 다른 스트림의 꼬리로 사용된다면(stream-cons의 두 번째 매개변수로), 새 요소들은 계약으로 검사되지 않지만 꼬리의 요소들은 여전히 강제됩니다.
base 패키지의 6.1.1.8 버전에서 추가되었습니다.
더 알아보기
- 시퀀스(Sequences) 관련 문서
- 제네릭 인터페이스(Generic Interfaces) 관련 문서
- Racket Guide의 스트림 관련 내용