Scala 中结合的笛卡尔积和映射

发布于 2024-10-09 00:40:44 字数 963 浏览 4 评论 0原文

这是以下内容的后续内容: 将一组字符串集合展开为笛卡尔积在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]]<类型的输入/code> 并返回一个 Set[B]。即在组合元素的同时进行类型转换。

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

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

我无法提出一个实现。有人有答案吗?

This is a followup to: Expand a Set of Sets of Strings into Cartesian Product in Scala

The idea is you want to take:

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

and get back:

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

A general solution is:

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

used as follows:

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

Theoretically, there should be a way to take input of type Set[Set[A]] and get back a Set[B]. That is, to convert the type while combining the elements.

An example usage would be to take in sets of strings (as above) and output the lengths of their concatenation. The f function in combine would something of the form:

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

I was not able to come up with an implementation. Does anyone have an answer?

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

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

发布评论

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

评论(2

错爱 2024-10-16 00:40:44

如果您确实希望组合器函数执行映射,则可以使用 fold 但作为 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 类型类,以及许多其他用于函数式编程的好东西。

If you really want your combiner function to do the mapping, you can use a fold but as Craig pointed out you'll have to provide a seed value:

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(_) } 
    }

The fact that you need such a seed value follows from the combiner/mapper function type (B, A) => B (or, as a curried function, B => A => B). Clearly, to map the first A you encounter, you're going to need to supply a B.

You can make it somewhat simpler for callers by using a Zero type class:

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

Then the combine method can be defined as:

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(_) }
    }

Usage:

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

Scalaz provides a Zero type class, along with a lot of other goodies for functional programming.

呢古 2024-10-16 00:40:44

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

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

The problem that you're running into is that reduce(Left|Right) takes a function (A, A) => A which doesn't allow you to change the type. You want something more like foldLeft which takes (B, A) ⇒ B, allowing you to accumulate an output of a different type. folds need a seed value though, which can't be an empty collection here. You'd need to take xs apart into a head and tail, map the head iterable to be Iterable[B], and then call foldLeft with the mapped head, the tail, and some function (B, A) => B. That seems like more trouble than it's worth though, so I'd just do all the mapping up front.

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
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文