双眼皮功能型图案

发布于 2024-12-01 16:41:57 字数 1000 浏览 4 评论 0原文

让玩具类Counter如:

class Counter private( val next: Int, val str2int: Map[String,Int] ) {
  def apply( str: String ): (Int,Counter)  = str2int get str match {
    case Some(i) => ( i, this )
    case None => ( next, new Counter( next+1, str2int + (str -> next) ) )
  }
}
object Counter {
  def apply() = new Counter( 0, Map() )
}

该类提供了String和自然数之间的映射,每次查询新的String时都会延迟扩展该映射。

然后我可以编写一个方法,可以将字符串序列转换为整数序列,从而在遍历期间更新映射。我得到的第一个实现是使用 foldLeft

def toInt( strs: Seq[String], counter: Counter ): ( Seq[Int], Counter ) =
  strs.foldLeft( (Seq[Int](), counter) ) { (result, str) =>
    val (i, nextCounter) = result._2( str )
    ( result._1 :+ i, nextCounter )
  }

这按预期工作:

val ss = Seq( "foo", "bar", "baz", "foo", "baz" )
val is = toInt( ss, Counter() )._1
            //is == List(0, 1, 2, 0, 2)

但我对 toInt 实现不是很满意。问题是我折叠了两个不同的值。是否有函数式编程模式来简化实现?

Let the toy-class Counter such as:

class Counter private( val next: Int, val str2int: Map[String,Int] ) {
  def apply( str: String ): (Int,Counter)  = str2int get str match {
    case Some(i) => ( i, this )
    case None => ( next, new Counter( next+1, str2int + (str -> next) ) )
  }
}
object Counter {
  def apply() = new Counter( 0, Map() )
}

This class provides a mapping between a String and a natural number, the mapping is extended lazily each time a new String is queried.

I can then write a method which can convert a Seq of Strings in a Seq of Ints, updating the mapping during traversal. The first implementation I got is with foldLeft:

def toInt( strs: Seq[String], counter: Counter ): ( Seq[Int], Counter ) =
  strs.foldLeft( (Seq[Int](), counter) ) { (result, str) =>
    val (i, nextCounter) = result._2( str )
    ( result._1 :+ i, nextCounter )
  }

This works as intended:

val ss = Seq( "foo", "bar", "baz", "foo", "baz" )
val is = toInt( ss, Counter() )._1
            //is == List(0, 1, 2, 0, 2)

But I am not very satisfied about toInt implementation. The problem is that I am folding on two different values. Is there a functional programming pattern to simplify the implementation ?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

一念一轮回 2024-12-08 16:41:57

您正在寻找的模式是 State monad:

import scalaz._
import Scalaz._

case class Counter(next: Int = 0, str2int: Map[String,Int] = Map()) {
  def apply( str: String ): (Counter, Int) = (str2int get str) fold (
    (this, _),
    (new Counter(next+1, str2int + (str -> next)), next)
  )}

type CounterState[A] = State[Counter, A]

def count(s: String): CounterState[Int] = state(_(s))

def toInt(strs: Seq[String]): CounterState[Seq[Int]] =
  strs.traverse[CounterState, Int](count)

那里的类型注释很不幸,也许可以通过某种方式消除它。无论如何,这是一个运行过程:

scala> val ss = Seq( "foo", "bar", "baz", "foo", "baz" )
ss: Seq[java.lang.String] = List(foo, bar, baz, foo, baz)

scala> val is = toInt(ss) ! Counter()
is: Seq[Int] = List(0, 1, 2, 0, 2)

The pattern you're looking for is the State monad:

import scalaz._
import Scalaz._

case class Counter(next: Int = 0, str2int: Map[String,Int] = Map()) {
  def apply( str: String ): (Counter, Int) = (str2int get str) fold (
    (this, _),
    (new Counter(next+1, str2int + (str -> next)), next)
  )}

type CounterState[A] = State[Counter, A]

def count(s: String): CounterState[Int] = state(_(s))

def toInt(strs: Seq[String]): CounterState[Seq[Int]] =
  strs.traverse[CounterState, Int](count)

The type annotation there is unfortunate, and maybe it can be eliminated somehow. Anyway, here's a run of it:

scala> val ss = Seq( "foo", "bar", "baz", "foo", "baz" )
ss: Seq[java.lang.String] = List(foo, bar, baz, foo, baz)

scala> val is = toInt(ss) ! Counter()
is: Seq[Int] = List(0, 1, 2, 0, 2)
献世佛 2024-12-08 16:41:57

您可以通过进行更多模式匹配来使折叠看起来更好一点:

strs.foldLeft((Seq[Int](), counter)) { case ((xs,counter), str) =>
  val (i, nextCounter) = counter(str)
  (xs :+ i, nextCounter)
}

然后,如果您有 管道运算符 |> 在某处定义,并且您对 /: 别名感到满意>向左折叠,你 就可以使其变得紧凑且可读。

((Seq[Int](), counter) /: strs) { case ((xs,counter), str) =>
  counter(str) |> { case (i,nextCounter) => (xs +: i, nextCounter) }
}

一旦您熟悉了语法,

You can make the fold you've got look quite a bit nicer by doing more pattern matching:

strs.foldLeft((Seq[Int](), counter)) { case ((xs,counter), str) =>
  val (i, nextCounter) = counter(str)
  (xs :+ i, nextCounter)
}

And then if you have the pipe operator |> defined somewhere, and you're comfortable with the /: alias for foldLeft, you can make that

((Seq[Int](), counter) /: strs) { case ((xs,counter), str) =>
  counter(str) |> { case (i,nextCounter) => (xs +: i, nextCounter) }
}

which is, once you're familiar with the syntax, compact and readable.

假面具 2024-12-08 16:41:57

我认为 state monad 就是您正在寻找的。

I think the state monad is what you're looking for.

对风讲故事 2024-12-08 16:41:57

折叠两个值并没有什么问题。它可以稍微改进:

import scalaz._
import Scalaz._

def toInt( strs: Seq[String], counter: Counter ): ( Seq[Int], Counter ) =
  strs.foldLeft( (Seq[Int](), counter) ) { case ((xs, counter), str) =>
    counter(str).mapElements(xs :+ _, identity)
  }

或者,如果你愿意,

def toInt( strs: Seq[String], counter: Counter ): ( Seq[Int], Counter ) =
  strs.foldLeft( (Seq[Int](), counter) ) { case ((xs, counter), str) =>
    (xs :+ (_: Int)) <-: counter(str)
  }

或者甚至,

def toInt( strs: Seq[String], counter: Counter ): ( Seq[Int], Counter ) =
  strs.foldLeft( (Seq[Int](), counter) ) { case (result, str) =>
    result.fold((xs, counter) =>
      counter(str).mapElements(xs :+ _, identity))
  }

Nothing wrong with folding on two values. It can be slightly improved:

import scalaz._
import Scalaz._

def toInt( strs: Seq[String], counter: Counter ): ( Seq[Int], Counter ) =
  strs.foldLeft( (Seq[Int](), counter) ) { case ((xs, counter), str) =>
    counter(str).mapElements(xs :+ _, identity)
  }

Or, if you prefer,

def toInt( strs: Seq[String], counter: Counter ): ( Seq[Int], Counter ) =
  strs.foldLeft( (Seq[Int](), counter) ) { case ((xs, counter), str) =>
    (xs :+ (_: Int)) <-: counter(str)
  }

Or even,

def toInt( strs: Seq[String], counter: Counter ): ( Seq[Int], Counter ) =
  strs.foldLeft( (Seq[Int](), counter) ) { case (result, str) =>
    result.fold((xs, counter) =>
      counter(str).mapElements(xs :+ _, identity))
  }
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文