【问题标题】:Cartesian Product and Map Combined in Scala在 Scala 中结合笛卡尔积和地图
【发布时间】:2011-05-29 16:45:06
【问题描述】:

这是对Expand a Set of Sets of Strings into Cartesian Product in 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]。即在组合元素的同时转换类型。

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

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

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

【问题讨论】:

    标签: scala cartesian-product


    【解决方案1】:

    如果您真的希望您的组合器函数进行映射,您可以使用fold,但作为Craig pointed out,您必须提供一个种子值:

    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 类型类,以及许多其他用于函数式编程的好东西。

    【讨论】:

    • 如果字符串的零是“”,并且组合函数将“&”放在字符串之间,那么此解决方案将导致输出中每个字符串的前面有一个额外的“&”(回到OP)。
    【解决方案2】:

    您遇到的问题是 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
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2017-06-02
      • 2015-01-28
      • 2020-07-05
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多