Scala视图——非严格与懒惰

Scala视图——非严格与懒惰,scala,lazy-evaluation,Scala,Lazy Evaluation,我试图创建一个对象的“惰性”映射(实际上,它们是参与者,但我用一个更简单的例子来提问) 从某种意义上说,Scala视图是懒惰的。但他们的懒惰实际上是不严格的。也就是说,这些值实际上是按名称调用的,这又意味着在需要时,通过调用Function0(无参数函数)来计算这些值 我感兴趣的是一个缓慢评估的集合,但只评估一次。以下是我想要的东西: val x = Map(1->2, 2->2).view val y = x map {case (k,v) => (k,{println("H

我试图创建一个对象的“惰性”映射(实际上,它们是参与者,但我用一个更简单的例子来提问)

从某种意义上说,Scala视图是懒惰的。但他们的懒惰实际上是不严格的。也就是说,这些值实际上是按名称调用的,这又意味着在需要时,通过调用Function0(无参数函数)来计算这些值

我感兴趣的是一个缓慢评估的集合,但只评估一次。以下是我想要的东西:

val x = Map(1->2, 2->2).view
val y = x map {case (k,v) => (k,{println("Hello");v.toString})}
val z1 = y.find{case (k,_) => k==1}
val z2 = y.find{case (k,_) => k==1}
当我将其放入Scala工作表时,我得到的是:

x: scala.collection.IterableView[(Int, Int),scala.collection.immutable.Map[Int,Int]] = IterableView(...)
y: scala.collection.IterableView[(Int, String),Iterable[_]] = IterableViewM(...)
Hello
z1: Option[(Int, String)] = Some((1,1))
Hello
z2: Option[(Int, String)] = Some((1,1))
一切都是应该的。除了我不想看到第二个“你好”。换句话说,我只希望在需要时调用一次映射函数(toString)


有人对如何实现我的目标有什么建议吗?这并不是非常重要,但我很好奇是否可以做到。

我会提出替代方案,而不是
懒惰。
如果您的
v
将是函数而不是值,该怎么办。 在这种情况下,您可以在需要时随时控制执行,而无需依赖集合的惰性


valy=xmap{case(k,v)=>(k,()=>{println(“Hello”);v.toString}}}

我会提出替代解决方案,而不是
懒惰。
如果您的
v
将是函数而不是值,该怎么办。 在这种情况下,您可以在需要时随时控制执行,而无需依赖集合的惰性

val y=x map{case(k,v)=>(k,()=>{println(“Hello”);v.toString}}
使用
流几乎可以得到您想要的:

scala> val x = TreeMap(1->2, 2->2) // to preserve order
x: scala.collection.immutable.TreeMap[Int,Int] = Map(1 -> 2, 2 -> 2)

scala> val y = x.toStream map {case (k,v) => (k,{println(s"Hello $k");v.toString})}
Hello 1
y: scala.collection.immutable.Stream[(Int, String)] = Stream((1,2), ?)

scala> y.find{case (k,_) => k==1}
res8: Option[(Int, String)] = Some((1,2))

scala> y.find{case (k,_) => k==2}
Hello 2
res9: Option[(Int, String)] = Some((2,2))
如您所见,第一个元素是严格评估的,但其他元素是按需评估和记忆的

如果将流本身设置为
惰性val
,则可以得到所需的:

scala>   val x = TreeMap(1->2, 2->2) // to preserve order
x: scala.collection.immutable.TreeMap[Int,Int] = Map(1 -> 2, 2 -> 2)

scala>   lazy val y = x.toStream map {case (k,v) => (k,{println(s"Hello $k");v.toString})}
y: scala.collection.immutable.Stream[(Int, String)] = <lazy>

scala>   y.find{case (k,_) => k==1}
Hello 1
res10: Option[(Int, String)] = Some((1,2))

scala>   y.find{case (k,_) => k==1}
res11: Option[(Int, String)] = Some((1,2))
我认为你不可能有一个(真正)懒惰的地图,但如果有人证明我错了,我会很高兴:)


编辑: 通过如下方式包装值,可以获得(某种)惰性映射:

class Lazy[T](x: => T) {
  lazy val value = x
  override def toString = value.toString
}

object Lazy {
  implicit def toStrict[T](l: Lazy[T]): T = l.value
}

val x = TreeMap(1->2, 2->2)
lazy val y = x map {case (k,v) => (k, new Lazy({println(s"Hello $k");v.toString}))}

y.find{case (k,v) => v.indexOf("x");k==1} // let's use v to evaluate it, otherwise nothing gets printed
y.find{case (k,v) => v.indexOf("x");k==1}
隐式转换允许您使用值,就像它们是原始类型一样

使用
几乎可以得到您想要的:

scala> val x = TreeMap(1->2, 2->2) // to preserve order
x: scala.collection.immutable.TreeMap[Int,Int] = Map(1 -> 2, 2 -> 2)

scala> val y = x.toStream map {case (k,v) => (k,{println(s"Hello $k");v.toString})}
Hello 1
y: scala.collection.immutable.Stream[(Int, String)] = Stream((1,2), ?)

scala> y.find{case (k,_) => k==1}
res8: Option[(Int, String)] = Some((1,2))

scala> y.find{case (k,_) => k==2}
Hello 2
res9: Option[(Int, String)] = Some((2,2))
如您所见,第一个元素是严格评估的,但其他元素是按需评估和记忆的

如果将流本身设置为
惰性val
,则可以得到所需的:

scala>   val x = TreeMap(1->2, 2->2) // to preserve order
x: scala.collection.immutable.TreeMap[Int,Int] = Map(1 -> 2, 2 -> 2)

scala>   lazy val y = x.toStream map {case (k,v) => (k,{println(s"Hello $k");v.toString})}
y: scala.collection.immutable.Stream[(Int, String)] = <lazy>

scala>   y.find{case (k,_) => k==1}
Hello 1
res10: Option[(Int, String)] = Some((1,2))

scala>   y.find{case (k,_) => k==1}
res11: Option[(Int, String)] = Some((1,2))
我认为你不可能有一个(真正)懒惰的地图,但如果有人证明我错了,我会很高兴:)


编辑: 通过如下方式包装值,可以获得(某种)惰性映射:

class Lazy[T](x: => T) {
  lazy val value = x
  override def toString = value.toString
}

object Lazy {
  implicit def toStrict[T](l: Lazy[T]): T = l.value
}

val x = TreeMap(1->2, 2->2)
lazy val y = x map {case (k,v) => (k, new Lazy({println(s"Hello $k");v.toString}))}

y.find{case (k,v) => v.indexOf("x");k==1} // let's use v to evaluate it, otherwise nothing gets printed
y.find{case (k,v) => v.indexOf("x");k==1}

隐式转换允许您使用值,就好像它们是原始类型一样

我不知道有哪种集合API提供这种惰性。但是,我认为通过如下所述的功能记忆,您可以实现您想要的:


我不知道有哪个集合API提供这种惰性。但是,我认为通过如下所述的功能记忆,您可以实现您想要的:


z1
z2
之间有什么区别?@rumoku没有区别,他只是想记忆y,这样每个元素就不会调用map函数两次
z1
z2
之间有什么区别?@rumoku没有区别,他只是想记忆y,这样每个元素的map函数就不会被调用两次是的,谢谢,我一直在想这样的事情。正如你所指出的,它并没有达到我真正想要的效果,但是它可以完成我的工作,如果有点不雅观的话:)是的,谢谢,我在想类似的事情。正如你所指出的,它并没有达到我真正想要的效果,但它可以完成工作,如果有点不雅观的话:)谢谢你(更新)的答案。你为此做了不少工作,我真的很感激。它似乎实现了我所期待的(如此上进的投票)。我不太清楚为什么要在谓词中插入v.indexOf(“x”),但我删除了它,它正常工作了。然而,底线是它并不简单(或优雅),在记忆模式上也没有太大改进。谢谢:)我以indexOf(“x”)为例,它写在注释中:
让我们用v来评估它,否则什么也不会打印出来。如果您从未以某种方式使用该值,则不会看到任何打印:)感谢您(更新)的答案。你为此做了不少工作,我真的很感激。它似乎实现了我所期待的(如此上进的投票)。我不太清楚为什么要在谓词中插入v.indexOf(“x”),但我删除了它,它正常工作了。然而,底线是它并不简单(或优雅),在记忆模式上也没有太大改进。谢谢:)我以indexOf(“x”)为例,它写在注释中:
让我们用v来评估它,否则什么也不会打印出来。如果您从未以某种方式使用该值,则不会看到任何打印:)