6

这是后续行动:在 Scala 中将一组字符串扩展为笛卡尔积

这个想法是你想要采取:

val sets = Set(Set("a","b","c"), Set("1","2"), Set("S","T"))

然后回来:

Set("a&1&S", "a&1&T", "a&2&S", ..., "c&2&T")

一个通用的解决方案是:

def combine[A](f:(A, A) => A)(xs:Iterable[Iterable[A]]) =
    xs.reduceLeft { (x, y) => x.view.flatMap {a => y.map(f(a, _)) } } 

使用如下:

val expanded = combine{(x:String, y:String) => x + "&" + y}(sets).toSet

从理论上讲,应该有一种方法来获取类型的输入Set[Set[A]]并返回一个Set[B]. 也就是说,在组合元素的同时转换类型。

一个示例用法是接收字符串集(如上)并输出它们连接的长度。中的f函数combine将采用以下形式:

(a:Int, b:String) => a + b.length 

我无法提出实施方案。有人有答案吗?

4

2 回答 2

9

如果您真的希望您的组合器功能进行映射,您可以使用 afold但正如Craig 指出的那样,您必须提供一个种子值:

def combine[A, B](f: B => A => B, zero: B)(xs: Iterable[Iterable[A]]) =         
    xs.foldLeft(Iterable(zero)) { 
       (x, y) => x.view flatMap { y map f(_) } 
    }

您需要这样一个种子值的事实来自于组合器/映射器函数类型(B, A) => B(或者,作为一个柯里化函数,B => A => B)。显然,要映射A您遇到的第一个,您需要提供一个B.

Zero您可以使用类型类使调用者更简单一些:

trait Zero[T] {
   def zero: T
}
object Zero {
   implicit object IntHasZero extends Zero[Int] {
      val zero = 0
   }
   // ... etc ...
}

那么combine方法可以定义为:

def combine[A, B : Zero](f: B => A => B)(xs: Iterable[Iterable[A]]) =         
    xs.foldLeft(Iterable(implicitly[Zero[B]].zero)) { 
       (x, y) => x.view flatMap { y map f(_) }
    }

用法:

combine((b: Int) => (a: String) => b + a.length)(sets)

Scalaz提供了一个Zero类型类,以及许多其他用于函数式编程的好东西。

于 2010-12-23T00:56:08.327 回答
7

您遇到的问题是 reduce(Left|Right) 采用一个函数 (A, A) => A ,它不允许您更改类型。你想要一些更像 foldLeft 的东西,它需要 (B, A) ⇒ B,允许你累积不同类型的输出。folds 需要一个种子值,这里不能是一个空集合。您需要将 xs 拆分为头部和尾部,将头部可迭代映射为 Iterable[B],然后使用映射的头部、尾部和一些函数 (B, A) => B 调用 foldLeft。不过,这似乎比它的价值更麻烦,所以我只需要预先完成所有映射。

def combine[A, B](f: (B, B) => B)(g: (A) => B)(xs:Iterable[Iterable[A]]) =
  xs.map(_.map(g)).reduceLeft { (x, y) => x.view.flatMap {a => y.map(f(a, _)) } }
val sets = Set(Set(1, 2, 3), Set(3, 4), Set(5, 6, 7))
val expanded = combine{(x: String, y: String) => x + "&" + y}{(i: Int) => i.toString}(sets).toSet
于 2010-12-22T23:31:08.793 回答