Есть ли в Scala эквивалент более общей функции отображения Python? - PullRequest
9 голосов
/ 16 апреля 2010

Я знаю, что в списках Scala есть реализация map с подписью (f: (A) => B):List[B] и реализация foreach с подписью (f: (A) => Unit):Unit, но я ищу что-то, что принимает несколько итераций так же, как Python map принимает несколько итераций.

Я ищу что-то с подписью (f: (A,B) => C, Iterable[A], Iterable[B] ):Iterable[C] или эквивалентной. Есть ли библиотека, где это существует, или аналогичный способ сделать подобное?

Edit:

Как предложено ниже, я мог бы сделать

val output = myList zip( otherList ) map( x => x(0) + x(1) )

но это создает временный список между шагами. Если комментатор разместил бы сообщение, я мог бы отозвать его (подсказка, подсказка), но есть ли другой способ?

Ответы [ 4 ]

12 голосов
/ 16 апреля 2010

В Scala 2.8 в Tuple2 и Tuple3 есть метод zipped, который не создает временную коллекцию. Вот пример использования:

Welcome to Scala version 2.8.0.r21561-b20100414020114 (Java HotSpot(TM) Client VM, Java 1.6.0_18).
Type in expressions to have them evaluated.
Type :help for more information.

scala> val xs = 0 to 9
xs: scala.collection.immutable.Range.Inclusive with scala.collection.immutable.Range.ByOne = Range(0, 1, 2, 3, 4, 5, 6, 7, 8, 9)

scala> val ys = List.range(0,10)
ys: List[Int] = List(0, 1, 2, 3, 4, 5, 6, 7, 8, 9)

scala> val zs = Array.range(0,10)
zs: Array[Int] = Array(0, 1, 2, 3, 4, 5, 6, 7, 8, 9)

scala> (xs,ys).zipped.map{ _+_ }
res1: scala.collection.immutable.IndexedSeq[Int] = Vector(0, 2, 4, 6, 8, 10, 12, 14, 16, 18)

scala> (zs,ys,xs).zipped.map{ _+_+_ }
res2: Array[Int] = Array(0, 3, 6, 9, 12, 15, 18, 21, 24, 27)

scala>

Существует метод zip как в Tuple2, так и в Tuple3. xs.zip (ys) совпадает с (xs, ys) .zip

Примечание: Существует также некоторая нехватка в (xs, ys) .zip и (xs, ys) .zipped, убедитесь, что xs не может быть бесконечным потоком. Для получения дополнительной информации перейдите на Билет # 2634 . У меня есть сообщение на nabble.com несколько дней назад, в котором отражено мое мнение о том, как исправить этот билет.

11 голосов
/ 16 апреля 2010

Функция, которую вы ищете, обычно называется zipWith. К сожалению, его нет в стандартных библиотеках, но его довольно легко написать:

def zipWith[A,B,C](f: (A,B) => C, a: Iterable[A], b: Iterable[B]) =
  new Iterable[C] {
    def elements = (a.elements zip b.elements) map f.tupled
  }

Это будет проходить только один раз, поскольку реализации для zip и map на итераторах полностью ленивы.

Но зачем останавливаться на Iterable? Это имеет еще более общую форму. Мы могли бы объявить интерфейс для всех структур данных, которые могут быть заархивированы таким образом.

trait Zip[F[_]] {
  def zipWith[A,B,C](f: (A,B) => C, a: F[A], b: F[B]): F[C]
}

Например, мы можем архивировать функции:

trait Reader[A] {
  type Read[B] = (A => B)
}

def readerZip[T] = new Zip[Reader[T]#Read] {
  def zipWith[A,B,C](f: (A,B) => C, a: T => A, b: T => B): T => C =
    (t: T) => f(a(t),b(t))
}

Оказывается, есть еще более общее выражение этого типа. В общем случае конструкторами типов, которые допускают реализацию этого интерфейса, являются аппликативные функторы

trait Applicative[F[_]] {
  def pure[A](a: A): F[A]
  def map[A,B](f: A => B, a: F[A]): F[B]
  def ap[A,B](f: F[A => B], a: F[A]): F[B]
}

В этом случае реализация zipWith:

def zipWith[F[_],A,B,C](f: A => B => C, a: F[A], b: F[B])
                       (implicit m: Applicative[F]) =
  m.ap(m.map(f,a), b)

Это обобщает функции любой арности:

  m.ap(m.ap(m.ap(m.map(f,a), b), c), d)

Библиотека Scalaz предоставляет экземпляры Applicative для большого количества структур данных в стандартной библиотеке. Также предусмотрен удобный синтаксис для ap. В Scalaz эта функция называется <*>:

def zipWith[F[_]:Applicative,A,B,C](f: A => B => C, a: F[A], b: F[B]) =
  (a map f) <*> b
3 голосов
/ 16 апреля 2010

В объекте List в Scala 2.7 есть метод map2 (и 2.8, но он устарел в пользу zipped). Вы используете это так:

List.map2( List(1,2,3) , List(4,5,6) ) { _ * _ }  // Gives List(4,10,18)

Eastsun уже показал, как использовать zipped в 2.8 (который работает для всех коллекций, а не только для списков).

2 голосов
/ 16 апреля 2010

Ну, я не знаю синтаксис (f: (A,B) => C, Iterable[A], Iterable[B] ):Iterable[C] (а я знаю ничего из Scala), но если бы мне пришлось угадывать, это означало бы "Функция f с двумя итеративными аргументами A и B и возвращением итерируемого C". Я не уверен, подразумевает ли это, что все итерируемые элементы дают одинаковое количество элементов.

В Python, я думаю, вы ищете функцию zip :

>>> A = range(10, 15)
>>> B = range(1000, 1500, 100)
>>> zip(A, B)
[(10, 1000), (11, 1100), (12, 1200), (13, 1300), (14, 1400)]
>>> [a + b for a,b in zip(A, B)]
[1010, 1111, 1212, 1313, 1414]
Вывод

zip выполняется только при условии кратчайшей итерации:

>>> A=range(10, 12)
>>> zip(A, B)
[(10, 1000), (11, 1100)]

В любом случае, некоторые встроенные функции Python, которые все должны знать, но легко пропускают: enumerate, map, reduce и zip. filter раньше был в этом списке, но в наши дни понятнее и гибче использовать понимание списка.

...