Кажется, что с operator.add
возникает путаница! Когда вы добавляете два списка вместе, правильным термином для этого является concat
, а не добавлять. operator.concat
- это то, что вам нужно использовать.
Если вы думаете о функциональности, это так же просто, как это ::
>>> list2d = ((1,2,3),(4,5,6), (7,), (8,9))
>>> reduce(operator.concat, list2d)
(1, 2, 3, 4, 5, 6, 7, 8, 9)
. Вы видите, что уменьшают уважение к типу последовательности, поэтому, когда вы поставляете кортеж, вы возвращаете кортеж. давайте попробуем со списком ::
>>> list2d = [[1,2,3],[4,5,6], [7], [8,9]]
>>> reduce(operator.concat, list2d)
[1, 2, 3, 4, 5, 6, 7, 8, 9]
Aha, вы получите список.
Как насчет производительности ::
>>> list2d = [[1,2,3],[4,5,6], [7], [8,9]]
>>> %timeit list(itertools.chain.from_iterable(list2d))
1000000 loops, best of 3: 1.36 µs per loop
from_iterable довольно быстро! Но это не сравнимо с уменьшением с concat.
>>> list2d = ((1,2,3),(4,5,6), (7,), (8,9))
>>> %timeit reduce(operator.concat, list2d)
1000000 loops, best of 3: 492 ns per loop
Мой предпочтительный способ избежать наследования класса case без дублирования кода несколько очевиден: создать общий (абстрактный) базовый класс:
abstract class Person {
def name: String
def age: Int
// address and other properties
// methods (ideally only accessors since it is a case class)
}
case class Employer(val name: String, val age: Int, val taxno: Int)
extends Person
case class Employee(val name: String, val age: Int, val salary: Int)
extends Person
Если вы хотите быть более мелкозернистым, сгруппируйте свойства в индивидуальные черты:
trait Identifiable { def name: String }
trait Locatable { def address: String }
// trait Ages { def age: Int }
case class Employer(val name: String, val address: String, val taxno: Int)
extends Identifiable
with Locatable
case class Employee(val name: String, val address: String, val salary: Int)
extends Identifiable
with Locatable
идеально подходят для объектов значений, то есть объектов, которые не меняют никаких свойств и могут сравниваться с равными.
Но реализация equals в присутствии наследования довольно сложна. Рассмотрим два класса:
class Point(x : Int, y : Int)
и
class ColoredPoint( x : Int, y : Int, c : Color) extends Point
Таким образом, согласно определению ColorPoint (1,4, красный) должен быть равен Точке (1,4 ) они в то же время суть. Таким образом, ColorPoint (1,4, синий) также должен быть равен Point (1,4), правильно? Но, конечно, ColorPoint (1,4, красный) не должен совпадать с ColorPoint (1,4, синий), потому что они имеют разные цвета. Там вы идете, одно основное свойство отношения равенства сломано.
update
Вы можете использовать наследование от признаков, решающих множество проблем, как описано в другом ответе. Еще более гибкой альтернативой часто является использование классов типов. См. . Каковы классы типов в Scala, которые полезны для? или http://www.youtube.com/watch?v=sVMES4RZF-8
Поскольку это интересная тема для многих, позвольте мне пролить свет здесь.
Вы можете пойти по следующему подходу:
// You can mark it as 'sealed'. Explained later.
sealed trait Person {
def name: String
}
case class Employee(
override val name: String,
salary: Int
) extends Person
case class Tourist(
override val name: String,
bored: Boolean
) extends Person
Да, вам нужно дублировать поля. Если вы этого не сделаете, было бы невозможно реализовать правильное равенство среди других проблем .
Однако вам не нужно дублировать методы / функции.
Если дублирование нескольких свойств очень важно для вас, используйте обычные классы, но помните, что они плохо подходят FP.
В качестве альтернативы вы могли бы использовать композицию вместо наследования:
case class Employee(
person: Person,
salary: Int
)
// In code:
val employee = ...
println(employee.person.name)
. Композиция - это правильная и звуковая стратегия, которую вы также должны учитывать.
И если вы задаетесь вопросом, что означает запечатанный признак - это то, что может быть расширен только в том же файле. То есть, два класса классов выше должны быть в одном файле. Это позволяет выполнять исчерпывающие проверки компилятора:
val x = Employee(name = "Jack", salary = 50000)
x match {
case Employee(name) => println(s"I'm $name!")
}
Дает ошибку:
warning: match is not exhaustive!
missing combination Tourist
Что действительно полезно. Теперь вы не забудете иметь дело с другими типами Person
s (люди). Это, по сути, класс Option
в Scala.
Если это не имеет для вас значения, вы можете сделать его незапечатанным и бросить классы case в свои собственные файлы. И, возможно, пойти с композицией.
В этих ситуациях я склонен использовать композицию вместо наследования. i .e.
sealed trait IVehicle // tagging trait
case class Vehicle(color: String) extends IVehicle
case class Car(vehicle: Vehicle, doors: Int) extends IVehicle
val vehicle: IVehicle = ...
vehicle match {
case Car(Vehicle(color), doors) => println(s"$color car with $doors doors")
case Vehicle(color) => println(s"$color vehicle")
}
Очевидно, вы можете использовать более сложную иерархию и совпадения, но, надеюсь, это дает вам представление. Ключ состоит в том, чтобы использовать вложенные экстракторы, которые предоставляют классы классов