2010-01-19 4 views
47

스칼라에는 반복자와 매우 유사한 Stream 클래스가 있습니다. 주제 Difference between Iterator and Stream in Scala?은 둘 사이의 유사점과 차이점에 대한 통찰력을 제공합니다.스칼라에서 스트림의 사용 사례

스트림을 사용하는 방법을 보는 것은 매우 간단하지만 다른 이슈 대신에 스트림을 사용하는 공용 사례 인 공통 사례가 많지 않습니다. 내가 지금이

아이디어는 :

  • 당신은 무한 급수의 사용을해야합니다. 하지만 이것은 일반적인 사용 사례처럼 보이지 않아 내 기준에 맞지 않습니다. (공통점이 있고 사각 지대가있는 경우 교정 해주십시오.)
  • 각 요소를 계산해야하지만 몇 차례 재사용 할 수있는 일련의 데이터가있는 경우. 이는 개발자 집단의 큰 하위 집합에 대해 개념적으로 쉽게 따라 할 수있는 목록에로드 할 수 있기 때문에 약합니다.
  • 아마도 많은 양의 데이터 또는 계산적으로 비싼 시리즈가 있고 필요한 항목이 모든 요소를 ​​방문 할 필요가 없을 확률이 높습니다. 그러나이 경우 Iterator는 여러 검색을 수행해야만 않는 한 좋은 일치가됩니다.이 경우 약간의 효율성이 떨어지더라도 목록을 사용할 수 있습니다.
  • 재사용해야하는 일련의 복잡한 데이터가 있습니다. 다시 목록을 사용할 수 있습니다. 이 경우 두 경우 모두 똑같이 사용하기 어렵고 모든 요소를로드 할 필요가 없으므로 스트림이 더 적합합니다. 그러나 다시는 그 공통점이 ... 그렇지 않습니까?

큰 사용을 놓친 적이 있습니까? 또는 대부분 개발자의 선호도입니까?

감사

답변

40

StreamIterator의 주요 차이점은 전자는 아니지만 후자는, 말하자면, 가변 및 '원 샷 "이라는 것이다. IteratorStream보다 더 나은 메모리 풋 프린트를 가지고 있지만, 이라는 사실은 불편할 수 있습니다.

def primeStream(s: Stream[Int]): Stream[Int] = 
    Stream.cons(s.head, primeStream(s.tail filter { _ % s.head != 0 })) 
val primes = primeStream(Stream.from(2)) 

그것은 쉽게뿐만 아니라 Iterator로 기록 될 수 있지만 Iterator 지금까지 계산 소수를 유지하지 됩니다

는 예를 들어,이 고전적인 소수 생성기를 가져 가라.

따라서 Stream의 중요한 측면 중 하나는 복제를 먼저하거나 반복하여 생성하지 않고도 다른 기능에 전달할 수 있다는 것입니다.

값 비싼 계산/무한 목록의 경우 마찬가지로 Iterator을 사용하여 이러한 작업을 수행 할 수 있습니다. 무한한 목록은 실제로 매우 유용합니다. 당신이 그것을 가지고 있지 않기 때문에 당신은 그것을 모릅니다. 그래서 시행 된 한정된 크기를 다루는 것보다 더 복잡한 알고리즘을 보았습니다.

+2

내가 추가하고 싶은 또 다른 차이점은 '스트림'이 헤드 요소에서 결코 게으르지 않다는 것입니다. 'Stream'의 헤드는 평가 된 형태로 저장됩니다. 요청 될 때까지 요소 (머리 포함)가 계산되지 않는 시퀀스가 ​​필요하다면 'Iterator'가 유일한 선택입니다. – Lii

+0

헤드 요소에 게으름이 아니라, 드롭하려는 모든 요소를 ​​평가합니다. 예 :''a "# :"b "# :"c "# :"d "# : Stream.empy [문자열] .drop (3)'은"a ","b " c "와"d ". 머리가되기 때문에 "d". – r90t

+0

소수 생성기에 대한 흥미로운 간결한 예.흥미롭게도, 간단한 스칼라 콘솔에서 생성 한 다음 4000 소수를 요구하면 (실제로는 그리 많지 않지만 2 초 내에 100K를 생성하는 대체 정의가 있음) "GC 오버 헤드 한도를 초과했습니다"오류가 발생하여 Scala가 충돌합니다 . –

17

Daniel의 답변 외에도 Stream은 단락 평가에 유용합니다.예를 들어, 내가 String을 가지고 Option[String]을 반환하는 함수의 거대한 세트를 가지고 가정, 그 중 하나가 작동 할 때까지 내가 그들을 실행을 유지하려면 :

val stringOps = List(
    (s:String) => if (s.length>10) Some(s.length.toString) else None , 
    (s:String) => if (s.length==0) Some("empty") else None , 
    (s:String) => if (s.indexOf(" ")>=0) Some(s.trim) else None 
); 

글쎄, 나는 확실히 을 실행하지 않으려는 전체 목록에 있으며, List에 "기능으로 처리하고 실행 중 하나가 None이 아닌 다른 값을 반환 할 때까지 실행하는 방법"이 없습니다. 무엇을해야합니까? 아마도이 :

이 목록을 소요하고 (실제로는 아무것도 평가하지 않는), 다음 기능을 적용한 결과 인 새로운 Stream을 정의 (하지만이 평가하지 않는 Stream로 취급
def transform(input: String, ops: List[String=>Option[String]]) = { 
    ops.toStream.map(_(input)).find(_ isDefined).getOrElse(None) 
} 

무엇이든 아직은) 정의 된 첫 번째 항목을 검색합니다. 여기서는 마술처럼 다시 돌아가서지도를 적용하고 원래 목록에서 올바른 데이터를 가져와야한다고 인식하고 Option[Option[String]]에서 언랩합니다 getOrElse을 사용하여 Option[String]으로 변경하십시오. 여기

은 예입니다 :

scala> transform("This is a really long string",stringOps) 
res0: Option[String] = Some(28) 

scala> transform("",stringOps) 
res1: Option[String] = Some(empty) 

scala> transform(" hi ",stringOps) 
res2: Option[String] = Some(hi) 

scala> transform("no-match",stringOps) 
res3: Option[String] = None 

그러나 그것은 작동합니까? 2.7의 구현은 종종 하나 오버 슈트, 불행하게도 당신이주의 것입니다. 우리는 우리의 기능으로 println을 넣으면 그래서 우리는 그들이 호출하는 경우, 우리는 (이 스칼라 2.8 함께

val stringOps = List(
    (s:String) => {println("1"); if (s.length>10) Some(s.length.toString) else None }, 
    (s:String) => {println("2"); if (s.length==0) Some("empty") else None }, 
    (s:String) => {println("3"); if (s.indexOf(" ")>=0) Some(s.trim) else None } 
); 
// (transform is the same) 

scala> transform("This is a really long string",stringOps) 
1 
res0: Option[String] = Some(28) 

scala> transform("no-match",stringOps)      
1 
2 
3 
res1: Option[String] = None 

를 얻을 말할 수있다 은 오류가 발생으로None의 긴 목록을 축적 할 수 있지만, 아마도이 여기 당신의 진정한 계산에 비해 저렴하다.)

+0

저는 실제로 그러한 예입니다. 그러나 이것은'Iterator'를 사용하여 쉽게 할 수 있습니다. 그래서 나는 요점 옆에 있다고 결정했습니다. –

+2

허가 됨. 이 스트림이 특정 스트림이 아니거나 동일한 스트림에 여러 호출을 사용하는 예제를 선택했음을 분명히해야합니다. –

2

Streamimmutable.Listmutable.List이다 Iterator에 관한 것입니다. 불변성을 선호하면 때때로 성능 저하를 초래하여 클래스의 버그를 방지합니다.

scalac 자체가 이러한 문제에 면역이되지 않습니다 : 다니엘로 http://article.gmane.org/gmane.comp.lang.scala.internals/2831

이 엄격 이상 게으름을 선호하는 알고리즘을 단순화하고 쉽게를 구성 할 수 있습니다 지적한다.

+1

물론 게으른 사람들에게는 코드의 예측 가능성을 줄이고 heisenbugs로 이어질 수 있으며 일부 알고리즘 클래스에는 심각한 성능 문제가있을 수 있다는 것이 중요합니다. –

7

실시간으로 일부 장치를 폴링하면 스트림이 더 편리하다고 상상할 수 있습니다.

GPS 추적기를 생각해 보면 실제 위치를 반환합니다. 5 분 안에 위치를 미리 계산할 수는 없습니다. 몇 분 동안 만 OpenStreetMap에서 경로를 실현하거나 사막이나 우림에서 6 개월 이상 탐험에 사용할 수도 있습니다.

또는 디지털 온도계 또는 하드웨어가 살아 있고 켜져있는 한 새로운 데이터를 반복적으로 반환하는 다른 종류의 센서 - 로그 파일 필터가 다른 예일 수 있습니다.

+1

Upvoted Stream의 유스 케이스. – nilskp