Y at-il un équivalent à Scala à la fonction carte plus générale de Python?
-
27-09-2019 - |
Question
Je sais que les listes de Scala ont carte mise en œuvre avec (f: (A) => B):List[B]
signature et foreach mise en œuvre avec (f: (A) => Unit):Unit
signature mais je suis à la recherche de quelque chose qui accepte plusieurs iterables de la même manière que le Python map accepte plusieurs iterables.
Je cherche quelque chose avec une signature de (f: (A,B) => C, Iterable[A], Iterable[B] ):Iterable[C]
ou équivalent. Y at-il une bibliothèque où cela existe ou d'une manière comparable de faire semblable?
Edit:
Comme suggéré ci-dessous je pouvais faire
val output = myList zip( otherList ) map( x => x(0) + x(1) )
mais qui crée une liste temporaire entre les étapes. Si le commentor signalerait je pouvais lui upvote (indice, indice), mais est-il une autre façon?
La solution
En scala 2.8, il existe une méthode appelée zippée dans Tuple2 et Tuple3 qui évitent de créer la collection temporaire. Voici quelques exemple d'utilisation cas:
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>
Il existe une méthode zip dans les deux Tuple2 et Tuple3. xs.zip (ys) est le même que (xs, ys) .zip
Remarque: Il y a aussi une pénurie (xs, ys) et (xs .zip, YS) .zipped, assurez-vous que xs ne peut pas être un INFINITE Stream. Aller à Ticket # 2634 pour plus d'informations. Je un poste en nabble.com il y a quelques jours qui montre mes opinions sur la façon de corriger ce billet.
Autres conseils
La fonction que vous recherchez est généralement appelé zipWith
. Il est malheureusement pas fourni dans les bibliothèques standard, mais il est assez facile d'écrire:
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
}
Cela traverse une seule fois, puisque les mises en œuvre pour zip
et map
sur itérateurs sont entièrement paresseux.
Mais pourquoi arrêt à Iterable
? Cela a une forme encore plus générale. Nous pourrions déclarer une interface pour toutes les structures de données qui peuvent être compressés de cette façon.
trait Zip[F[_]] {
def zipWith[A,B,C](f: (A,B) => C, a: F[A], b: F[B]): F[C]
}
Par exemple, on peut comprimer les fonctions:
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))
}
Il se révèle être une expression encore plus générale de ce type. En général, les constructeurs de type qui permettent une mise en œuvre de cette interface sont foncteurs applicatifs
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]
}
est alors une mise en œuvre de zipWith ceci:
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)
Ceci généralise aux fonctions de tout arité:
m.ap(m.ap(m.ap(m.map(f,a), b), c), d)
La bibliothèque Scalaz fournit des cas Applicative pour un grand nombre de structures de données dans la bibliothèque standard . En outre, la syntaxe pratique est prévue pour ap
. En Scalaz, cette fonction est appelée <*>
:
def zipWith[F[_]:Applicative,A,B,C](f: A => B => C, a: F[A], b: F[B]) =
(a map f) <*> b
Il y a une map2
méthode dans l'objet List
à Scala 2.7 (et 2.8, mais il est dépréciée en faveur de zipped
). Vous pouvez l'utiliser comme ceci:
List.map2( List(1,2,3) , List(4,5,6) ) { _ * _ } // Gives List(4,10,18)
Eastsun est déjà montré comment utiliser zipped
2.8 (qui fonctionne sur toutes les collections, et pas seulement des listes).
Eh bien, je ne sais la (f: (A,B) => C, Iterable[A], Iterable[B] ):Iterable[C]
de syntaxe (et je sais que rien de Scala), mais si je devais deviner, cela signifierait « Une fonction < em> f prenant deux arguments itérables A et B et en retournant un iterable C ». Je ne sais pas si cela implique que tous les iterables donnent le même nombre d'éléments.
En Python, je pense que vous cherchez zip fonction :
>>> 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]
La sortie de zip
est seulement aussi longtemps que le plus court itérables:
>>> A=range(10, 12)
>>> zip(A, B)
[(10, 1000), (11, 1100)]
Quoi qu'il en soit, certaines fonctions Python intégré dans tout le monde a besoin de savoir, mais manque facilement: enumerate
, map
, reduce
et zip
. filter
utilisé pour être sur cette liste, mais il est plus clair et plus souple d'utiliser une liste de compréhension ces jours-ci.