Use-cases for Streams in Scala

后端 未结 4 1468
孤街浪徒
孤街浪徒 2020-12-12 13:18

In Scala there is a Stream class that is very much like an iterator. The topic Difference between Iterator and Stream in Scala? offers some insights into the similarities a

4条回答
  •  一生所求
    2020-12-12 13:30

    In addition to Daniel's answer, keep in mind that Stream is useful for short-circuiting evaluations. For example, suppose I have a huge set of functions that take String and return Option[String], and I want to keep executing them until one of them works:

    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
    );
    

    Well, I certainly don't want to execute the entire list, and there isn't any handy method on List that says, "treat these as functions and execute them until one of them returns something other than None". What to do? Perhaps this:

    def transform(input: String, ops: List[String=>Option[String]]) = {
      ops.toStream.map( _(input) ).find(_ isDefined).getOrElse(None)
    }
    

    This takes a list and treats it as a Stream (which doesn't actually evaluate anything), then defines a new Stream that is a result of applying the functions (but that doesn't evaluate anything either yet), then searches for the first one which is defined--and here, magically, it looks back and realizes it has to apply the map, and get the right data from the original list--and then unwraps it from Option[Option[String]] to Option[String] using getOrElse.

    Here's an example:

    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
    

    But does it work? If we put a println into our functions so we can tell if they're called, we get

    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
    

    (This is with Scala 2.8; 2.7's implementation will sometimes overshoot by one, unfortunately. And note that you do accumulate a long list of None as your failures accrue, but presumably this is inexpensive compared to your true computation here.)

提交回复
热议问题