9

現在、Java コードを Scala コードに変換しようとしています。課題は、変換された Scala コードが元の Java コードと比較して非常に非効率的なことをしないようにすることです。たとえば、次のコードを変換しようとする場合:

class Person {
    String name;
    Integer age;
    Character gender;
}

public class TestJava {
    public static void main(String[] args) {
        final List<Person> persons = new ArrayList<>();
        final List<Person> males = new ArrayList<>();
        final List<Person> aNames = new ArrayList<>();
        final List<Person> seniors = new ArrayList<>();
        for (final Person p: persons) {
            if (p.gender == 'm') {
                males.add(p);
            }
            if (p.age >= 60) {
                seniors.add(p);                
            }
            if (p.name.startsWith("a")) {
                aNames.add(p);
            }            
        }
    }
}

Java はミューテーションに依存しているため、このコードは論理的に見えます。しかし、コレクションを複数回 (この場合は 3 回) ループすることなく、これを Scala の同等のものに変換したいと考えています。

もちろん、Scala ライブラリの mutableListを使用して、Java と同じことを達成できますが、特定のシーケンス/コレクションから関数型/Scala の方法で複数のコレクションを生成できるかどうか疑問に思っていましnn基準カウントです。前もって感謝します!

4

6 に答える 6

5

純粋に機能的で不変の方法の 1 つは、述語によってコレクションをバケットに分割する汎用関数を使用することです。

case class Person(name: String, age: Int, gender: String)

def bucketsByPredicate(people: Seq[Person], predicates: Seq[Person => Boolean]) = {
  people.foldLeft(predicates.map(predicate =>
    (predicate, List.empty[Person])
  )) { case (predicates, person) =>
      predicates.map { case (predicate, members) =>
        (predicate, if(predicate(person)) person :: members else members)
      }
  }.map(_._2)
}

次に、使用例は次のようになります。

val olderThan60 = (p: Person) => p.age >= 60
val male = (p: Person) => p.gender == "m"
val Seq(olderThan60People, malePeople) = bucketsByPredicate(people, Seq(olderThan60, male))
于 2013-08-15T19:28:01.627 に答える
3
case class Person(gender:Sex,name:String,age:Int)

sealed trait Sex
case object Male extends Sex  
case object Female extends Sex

def part3(l:List[Person]) = {
  def aux(l:List[Person],acc:(List[Person],List[Person],List[Person])) : (List[Person],List[Person],List[Person]) = l match {
    case Nil => acc
    case head::tail => {
      val newAcc = (if (head.gender == Male) head :: acc._1 else acc._1,
                    if (head.age >= 60) head :: acc._2 else acc._2,
                    if (head.name startsWith "a") head :: acc._3 else acc._3)
      aux(tail,newAcc)
    }
  }
  val emptyTuple = (List[Person](),List[Person](),List[Person]())
  // It is (much) faster to reverse the input list and then prepend during to recursive loop.
  // prepend is O(1), append is O(n)
  aux(l.reverse,emptyTuple)
}

val (males,seniors,aNames) = part3(List(Person(Male,"abc",20),Person(Female,"def",61),Person(Male,"Nope",99)))
println(s"males : $males \nseniors : $seniors \naNames : $aNames")

// outputs
// males : List(Person(Male,abc,20), Person(Male,Nope,99))
// seniors : List(Person(Female,def,61), Person(Male,Nope,99))
// aNames : List(Person(Male,abc,20))

( List[Person] タプルはこれを非常に見苦しくします。結果の型エイリアスを定義したい場合があります。)

于 2013-08-15T20:16:47.367 に答える
3

これはかなり実用的です。人のリスト全体でパターン マッチを行い、各再帰呼び出しで条件をチェックし、必要に応じて結果に追加します。リストが使い果たされるまで。結果は、ListPerson詰め込まれた のTupleです。

class Person(val name: String, val age: Integer, val gender: Character){
  override def toString = name + " " + age + " " + gender
}

val alice = new Person("Alice", 18, 'f')
val bob = new Person("Bob", 18, 'm')
val charlie = new Person("Charlie", 60, 'm')
val diane = new Person("Diane", 65, 'f')
val fred = new Person("Fred", 65, 'm')

def filterPersons(persons: List[Person]) = {
  import scala.collection.mutable.{ListBuffer => LB}
  def filterPersonsR(persons: List[Person], males: LB[Person], anames: LB[Person], seniors: LB[Person]): Tuple3[LB[Person], LB[Person], LB[Person]] = persons match {
    case Nil => (males, anames, seniors)
    case h :: t => {
      filterPersonsR(t, if(h.gender == 'm') males += h else males, if(h.name.startsWith("A"))  anames += h else anames, if(h.age >= 60) seniors += h else seniors)
    }
  }
  filterPersonsR(persons, LB(), LB(), LB())
}

テスト中...

scala> filterPersons(List(alice, bob, charlie, diane, fred))
res25: (scala.collection.mutable.ListBuffer[Person], scala.collection.mutable.ListBuffer[Person], scala.collection.mutable.ListBuffer[Person]) = (ListBuffer(Bob 18 m, Charlie 60 m, Fred 65 m),ListBuffer(Alice 18 f),ListBuffer(Charlie 60 m, Diane 65 f, Fred 65 m))

scala> res25._1
res26: scala.collection.mutable.ListBuffer[Person] = ListBuffer(Bob 18 m, Charlie 60 m, Fred 65 m)

scala> res25._2
res27: scala.collection.mutable.ListBuffer[Person] = ListBuffer(Alice 18 f)

scala> res25._3
res28: scala.collection.mutable.ListBuffer[Person] = ListBuffer(Charlie 60 m, Diane 65 f, Fred 65 m)
于 2013-08-15T21:54:19.237 に答える
2
import scala.collection.immutable.List
import scala.collection.mutable.ListBuffer

case class Person(name: String, age: Int, gender: String)

def partition(persons: List[Person], predicates: List[Person => Boolean]): List[List[Person]] = {

    val bufs = List.fill(predicates.size)(new ListBuffer[Person])

    persons.foreach { person =>
        (0 until predicates.size).foreach{ i =>
            if (predicates(i)(person)) bufs(i) += person
        }
    }

    bufs map (_.toList)
}

val alice = new Person("Alice", 18, "f")
val bob = new Person("Bob", 18, "m")
val charlie = new Person("Charlie", 60, "m")
val diane = new Person("Diane", 65, "f")
val fred = new Person("Fred", 65, "m")

val olderThan60 = (p: Person) => p.age >= 60
val male = (p: Person) => p.gender == "m"
val nameStartsWith = (p: Person) => p.name.startsWith("A")

println(partition(List(alice, bob, charlie, diane, fred), List(olderThan60, male, nameStartsWith)))

このソリューションは純粋に機能的ではありませんが、より簡単です。変更可能なコレクション (ListBuffer など) がより適切に機能する状況はたくさんありますが、変更可能な状態を関数またはクラスの外に漏らさないようにしてください。読みやすさの利点は、純粋さを失う価値があります。

于 2013-08-16T01:33:53.937 に答える