SCALAの記事一覧

Top / scala

はじめに

このページは関数脳を作るべく、関数言語を題材にした99個の問題を解くのではなく、 解いてある回答を見て学ぶために作ったページです。

再帰的に解いてあると、関数言語だなーという気分になるのは、じぶんだけでしょうか?

まだ、全問といていませんが、極力1日1問のペースで解いていこうとおもっています。

目次

こんにちは、みなさんお元気ですか。
このページはプログラミング言語SCALAについてまとめたページです。
Androidアプリケーションの開発やクラウド開発、
そして企業のリソースをWebAPI化するなどの
プログラミングには信頼性や資源の多さからJavaが有効だとおもいますが、
SCALAはそのJavaアプリケーションをさらに効率よく開発する可能性を秘めています。

Scalaとは

Scalaは、JVM上で動作するオブジェクト指向+関数型言語で、
Javaとほぼ完全な相互利用が可能な言語です。
これまでの言語の集大成に近い言語です。
そのため、各言語の優れた概念を取り入れているわけです。
軽い気持ちで学ぶと、あちこちの言語の概念の基本を抑えたほうが近道なこともあり、
色々な言語の入門書をわたり歩くはめになります。
作者であるMartin Odersky教授はJavac開発の貢献者であるとともに、
Java5 Genericsの仕様策定にも参加しています。
というわけでSCALAはJava言語と親和性の高い関数型言語です。
コンパイルするとJavaのクラスファイルになるんですよ。
Javaのライブラリは膨大で他の言語に比べて日本語の処理方法がしっかりしています。
そしてコードが簡潔に書けるので生産性が高いといわれています。
たとえばBeanクラスを記述する場合、例は下記に記しますが、3分の1の
コーディングですみます。

その他の特徴

練習問題 S-99 Ninety Nine Scala Problems

何らかの練習問題をこなすことで力がつくとおもいます。自分で解くことも大切ですが模範回答から学ぶことも大切です。
このページは下記の
S-99 Ninety Nine Scala Problems

http://aperiodic.net/phil/scala/s-99/

の回答をつかって学習していくページです。
模範回答をみてえられる知識は実践向きの知識とも言えるかとおもいます。
パズルゲームを解いていくような気分で1日1問を解説していければとおもっています。

対象者

SCALAのインストールは完了し、HelloWorldを実行し終えた程度の読者を想定
SCALAをやるのは全くのはじめてという方向け
SCALAはコンパイルすると、JAVA言語のクラスを生成する関数型言語です。
型推論を行ってくれるので、マスターすれば生産性が向上します。

趣旨

有名な練習問題99問が回答付きで英語ながらも存在しているので、
回答をみながら、ポイントをまとめていこうかという趣旨です。
問題自体はリストを処理するAPIを知っている人向けなのですが、
問題が出てくる毎にそのメソッドを紹介していきます。

参考:リストを操作するAPI

https://www.scala-lang.org/docu/files/api/scala/List.html

ローカルにAPI資料をもってきたい場合

sbazというインストーラからダウンロードします。 名前の由来は「Scala Bazaars」です。 DOSプロンプトで操作します。 rem はDOSのコメント行を意味しています。

rem sbazのあるディレクトリにカレントディレクトリを移します。
cd C:\scala\bin
rem sbazを使えるようにします。
sbaz installed
rem 一覧を取得します。
sbaz available
rem ドキュメントをダウンロードします。
sbaz install scala-devel-docs

下記のフォルダにAPI資料をダウンロードできます。

C:\scala\doc\scala-devel-docs\api

1問

1問目は、回答を自分の環境で実行させること自体が学びが大きいです。

手順

インタプリタ形式で確認するには、

scalaでインタプリタ形式で :load .\P01.scala と入力すると 読み込んでくれます。

確認方法は

P01.last(List(1,2,3,4,5)) です。

最後の要素を取得する

.last

2問

2問とは関係ないのだが、今度はEclipseでSCALAを実行させてみたいとおもう。 そのためには下記のプラグインをインストールすることが必要だ。 Scala IDE for Eclipse http://www.scala-lang.org/node/94

Scala Eclipse Pluginの導入

Eclipse開いて、「Help -> Install New Software... -> 
Workwithフォルダに
Available Software -> Add Site..」でLocationに「http://www.scala-lang.org/scala-eclipse-plugin」と入れる
あとは、直感的にわかるので省略

確認用メイン

回答をエクリプスから実行するにはメインメソッドが必要ですので、 下記のような実行用のメソッドを用意するといいでしょう。

object HelloScala
{
	def main(args:Array[String])
	{ 
		println("Hello Scala!")
		println("P03")
		println(P03.nth(2,List(1,2,3,4)))
		System.exit(0)
	}
}

本題の2問目

 // But pattern matching also makes it easy.
 def penultimate[T](l: List[T]): T = l match {
   case e :: _ :: Nil => e
   case _ :: tail     => penultimate(tail)
   case _             => throw new NoSuchElementException
 }
 1問目と比較してみる
 def last[T](l: List[T]): T = l match {
   case e :: Nil  => e
   case _ :: tail => last(tail)
   case _         => throw new NoSuchElementException
 }

scalaは配列要素をパターンマッチできるようだ。

  2問目の最後から2つ目にマッチ
   case e :: _ :: Nil => e
  1問目の最後にマッチ
   case e :: Nil  => e
 
::は配列の要素間の区切り文字である。

配列の長さを取得する

.length

右からn個の要素を取得する

.takeRight(2) ()はapplyメソッドと同等らしいです。

先頭を取得する

.head

3問目

 回答に
(n, l) match {
  
}
という構文が使用されている
第一パラメータっぽい変数nがmatch内で使えるようになっている

n番目の要素を取得する

配列変数(n)

4問目

def内にdefを入れ子にできる

def foo = {
  def bar = {

  }
  bar
}

先頭以外の要素

.tail

5問目

配列を逆にする

.reverse

配列を連結する演算子::と:::

配列を連結する演算子には::と:::があって、知らないと困る

::の例

List(1,2,3) :: List(4,5,6)

結果

2番目の配列の先頭に格納される
List(List(1,2,3),4,5,6)

:::の例

List(1,2,3) ::: List(4,5,6)

結果

List(1,2,3,4,5,6)

配列の連結演算子::

match内での先頭とそれ以外の分割(効率はn^2なので小さい配列向き)

case h :: tail => reverse(tail) ::: List(h)

Nil=List() と List(1,2)=1::2::Nil

配列の書き方で
1 :: 2
と書くとエラーになるが
1 :: 2 :: Nil
と書くと
List(1,2)
として認識される

配列の要素をまずは、初期値を処理し、次からは次の要素と処理結果とを処理させる

ややこしいが、その分応用できる箇所をすっきりと記述できる。
.foldLeft
左からぱたぱた倒れていくような感じの関数で初期値と、
名前なしの関数を与えるようなイメージ
(List(2,3,4).foldLeft (1)) {(x,y) => x+y}

foldLeft・foldRightは別名があり、それぞれ「/:」「:\」とも書ける。

foldLeft は /: とも書けるので次のようにより簡潔に書ける
(1 /: List(2,3,4))(_+_)
初期値が先頭にきている感じだ。
()を閉じ終えてからくる()が、処理。

回答を理解できるようになったので解説してみる

というわけで、次の処理内容をみてみると以下のことが理解できる
def reverse[T](l: List[T]): List[T] = l.foldLeft(List[T]())((r, h) => h :: r)
List[T]()は初期値であり、空の配列である。
最初は空の要素と左の要素が処理され最初の要素がrに格納される
次の要素はhに格納され最初の要素はrなので、(2番目の要素,最初の要素)
が格納される。

応用:Beanを定義する。

ここまでは、配列の中身が数値とかだったので、もう少し込み入った情報を扱いたい場合もあるかとおもいますので、Beanの扱いについて説明いたします。

その前にオーバライトの書き方を紹介します。

Javaメソッドのオーバライド

defの前にoverrideをつけます。

override def toString="(name:"+name+ " value:"+value+")"

case classを使った定義方法

通常のBeanの設定方法

Beanの定義

class FieldValue(_name : String , _value : String) {
	val name = _name
	val value = _value
	override def toString="(name:"+name+ " value:"+value+")" 
}

コンストラクションを増やしたい場合

class FieldValue(_name : String , _value : String) {
	val name = _name
	val value = _value
	override def toString="(name:"+name+ " value:"+value+")" 
       def this(_name:String) = this(_name,null)
}

Beanインスタンスの作成

val field1 = new FieldValue("key1","data1")
val field2 = new FieldValue("key2","data2")
val field3 = new FieldValue("key2","data2")
val field4 = new FieldValue("key2","data2")

Beanの配列

List(field1,field2,field3,field4)

問6

答えは簡単に

def isPalindrome[T](l: List[T]): Boolean = l == l.reverse

だった。

scalaのfor文

配列以外でループさせたい場合につかうかもしれない

       for (x <- (0 to 10)){
           println(x)
      }

takeWhile

ちなみにループ中にループを抜けたい場合はtakeWhileをつかう

var continue = true
for (x <- (1 to 20).takeWhile(e => continue)) {
  println(x)
  continue = x < 5
}
var continue = true
(1 to 20).takeWhile(e => continue).foreach { x =>
  println(x)
  continue = x < 5
}

とりあえず、バクかなとおもって本家にバグ報告してみたところ下記の回答がかえってきました。

SCALAの本家から回答が帰ってきました。

this is the result of an intentional change. Range is strict now.

one way to get lazy behavior would be to insert toStream: "(1 to
20).toStream.takeWhile". in general, though, mixing for comprehensions and
side-effects is discouraged.

つまり下記のように(1 to 20)の部分を(1 to 20).toStreamとすればよいようです。

var continue = true
for (x <- (1 to 20).toStream.takeWhile(e => continue)) {
  println(x)
  continue = x < 5
}

配列の連結

配列の連結は先頭にしかできないので どうしても末尾にしたい場合は、 reverseをつかって先頭に追加してから、 またreverseでもどしたりする。

文字列格納用配列の宣言

var result :List[String] = List()

が正解で

var result = List() ......NG Error

だと、空配列しか受け付けない配列として宣言したことになるようです。

配列の追加

配列が先頭にしか追加できない
result = "a" :: result
だとエラーにならないけど 
result = result :: "a"
だとエラーになるってこと

戻り値のあるメソッドの書き方注意点

メソッドの戻り値がある場合は、メソッドの中身を書く際に={中身}とすること
そうしないと
illegal start of declaration
というエラーがかえってきます。

キャストの仕方

クラスの型を正しく指定しないといけませんので指定します。 BufferedSource?でキャストする方法 .asInstanceOf?[BufferedSource?].

動的変数を持つ関数の作り方

動的変数は try catch の中のプログラムをブロックとして、
あとで追加できるようにする仕組みで、ファイルを閉じるなどの
必ず行うような処理を簡潔に書くことができます。
def 動的関数名(引数)(動的変数 => Unit){

}

動的変数を持つ関数の使い方

 動的関数名(引数){(動的変数 ) =>
    処理内容
 }

文字列を配列に変換する

ファイルから読み込んだ文字列の場合、いったん配列にしてやらないといけない だから文字列を配列に変換するメソッドをつくってみました。

   
   def to_a(text:String):List[String] ={
       var result :List[String] = List()
       var len = text.length;
       for (x <- (0 to text.length -1)){
           result = text.substring(len-x-1,len-x) :: result
      }            
      result
   }

別解:JavaのtoCharArray?をつかう例。これだけでもいいけどArray型になってしまうため、型変換メソッドも用意しました。

   //ArrayをList配列に変換します。
   def toListoReverse(array:Array[Char]):List[Char]={
   	var result :List[Char] = List()
   	array.foreach{
   		x=>result = x :: result
   	}
   	result
   }
   //文字列を配列に変換します。
   def toList(string:String):List[Char]={
   	toListoReverse(string.toCharArray).reverse
   }

ファイルの読み込みを動的変数で簡潔にする

ファイルの読み込みはclose処理をいれなきゃいけないのですが、
動的関数をつかえば、簡潔に表現できます。
   import scala.io.Source
   import scala.io.BufferedSource
   import java.io._
   
   // ファイルの読み込みを動的変数で簡潔にする
   def read(path: String)(block: Source => Unit) ={
       var fis :FileInputStream =null
       var sou :Source =null
       
       try {
         fis= new FileInputStream(path)
         sou= Source.fromInputStream(fis)
         block(sou)
       }finally{
         sou.asInstanceOf[BufferedSource].close
         fis.close
       }
   }
   //readメソッドの使い方はこんな感じ  
   read("sample.txt"){(in:Source) => 
       for(line <- in.getLines){
           println(line.stripLineEnd)
       }
   }

ファイルをcloseする箇所が省けているのがわかります。

応用:ファイルの内容を一気に配列化するメソッド

   //ファイルをすべて読み配列で返します。
   def readAll(path: String):List[String]={
       var result :List[String] = List()
       read(path){(in:Source) => 
           for(line <- in.getLines){
               result = line.stripLineEnd :: result
           }
       } 
       result.reverse   
   }
   //使い方
   readAll("sample.txt").foreach(println _) 

とってもシンプルになりました。

問07

scala.Anyとは

Scala のすべてのクラスの継承元となる基底クラス (Int、Float、Double などの型やその他の数値型) は scala.Any 型です。

入れ子状態になっている配列の入れ子を解消してフラットな配列にする

flatMapをつかいます。flatMapは基礎的メソッドです。

 def flatten(l: List[Any]): List[Any] = l flatMap {
   case l: List[_] => flatten(l)
   case e => List(e)
 }

問08

値が一致する要素を削除する

match処理内のcase処理部分でつかいます。

tail.dropWhile(_ == e)

普通に式を評価しようとするとvalue dropwhile is not a member of Listとエラーになります。

問09

多重代入

ScalaもRubyのように多重代入ができるんですねぇ。

scala> val (aa,bb)=(11,22)
aa: Int = 11
bb: Int = 22

List形式の場合の多重代入

scala> var list = List(1,2,3)
list: List[Int] = List(1, 2, 3)
 
scala> var List(aa,bb,cc)=list
aa: Int = 1
bb: Int = 2
cc: Int = 3 

一致するシンボルと、それ以降を分離する。span

spanをつかいます。

scala> var l = List('a, 'a, 'a, 'a, 'b, 'c, 'c, 'a, 'a, 'd, 'e, 'e, 'e, 'e) scala> l.span(_ == l.head) res16: (List[Symbol], List[Symbol]) = (List('a, 'a, 'a, 'a),List('b, 'c, 'c, 'a,

'a, 'd, 'e, 'e, 'e, 'e))

問10

前回で定義したメソッドと連携して最小限のメソッド実装で問題を解いていきます そのためには前回定義したメソッドをimportします。

import P09.pack

インポートするためには参照元の.scalaファイルがコンパイルされている必要があります。 さもなくば、下記のエラーが出ます。 P09 is not a member of problem

というわけで、

scalac P09.scala

でコンパイルすればいいんですが、 今後プロジェクトでこの99の問題をコンパイルし続けるとディレクトリ内はソースとクラスファイルでごちゃごちゃになってしまいます。

それを防ぐためには、ソースファイルとクラスファイルの出力先を分けたたほうがいいですし、 そして問題の回答としてできてくる99個のファイルは一つのパッケージにしなきゃつらいですね。

となると、package化の必要があります。と、なると

で、いちいちscalacのオプションを書くのは面倒なのでバッチファイルをつくりました。 拡張子の.scalaは省略してもいいようにしてあります。

バッチファイルでの特定の拡張だけのパスを集める最小限的のサンプル

@echo off
set CP=.
set BASE=.
setlocal ENABLEDELAYEDEXPANSION
for %%f in (%BASE%\*.jar) do set CP=!CP!;%%f
echo %CP%

クラスパスの指定に偽ワイルドカード

Java SE 6 では、クラスパスの指定に偽ワイルドカードが使えるようになるようです。 なんで偽かっていうと

設定箇所はプロジェクトのディレクトリです。

ビルドツールの出番

でもimportするということは、ファイル同士に依存関係が出てくるということです。
ということは、 いまは例題を1から順番にやっているだけなので順番にコンパイルしていけばいいのですが、
業務でつかうとなれば、ビルドツールのANTまたは、MAVENをつかったほうがよさそうです。

ビルドツールMAVEN

ダウンロード http://maven.apache.org/download.html

仮に解凍したディレクトリの名称をmavenに変更して下記に設置したとします。 C:\maven

お決まりの操作なので バッチファイルとrubyでつくったスクリプトを用意しました。 なんで、rubyかって?単に簡潔に書けそうな気がしたからです。

mkprj.bat

SET PROJECT=%1
SET PACKAGE=%2
SET RUBYSCRIPT=c:\scala\bin\scalaproject.rb

@echo off
if exist %PROJECT% GOTO PROCESS1

SET CREATE=org.apache.maven.plugins:maven-archetype-plugin:1.0-alpha-7:create
SET PARAM=        -DarchetypeGroupId=org.scala-tools.archetypes
SET PARAM=%PARAM% -DarchetypeArtifactId=scala-archetype-simple
SET PARAM=%PARAM% -DarchetypeVersion=1.2
SET PARAM=%PARAM% -DremoteRepositories=http://scala-tools.org/repo-releases
mvn %CREATE% %PARAM% -DgroupId=%PACKAGE% -DartifactId=%PROJECT%

:PROCESS1
if exist %PROJECT%\pom.xml.bat GOTO PROCESS2
ruby %RUBYSCRIPT% c:\scala\bin\scalaproject.rb %PROJECT% %PACKAGE% %3

:PROCESS2

scalaproject.rb

#プロジェクト名を第一引数にとります。
#パッケージ名を第2引数にとります。
#バージョンを第3引数にとります。
puts "argv[0]:"+ARGV[1]

projectname =ARGV[1]
filename = projectname + "\\pom.xml" 

file = open(filename)
# 保存用バッファ
buffer = file.read().split("\n");
file.close

# バックアップ用ファイルを開く
bkup = File.open(filename + ".bak" , "w");
# ファイルから読み込んでバックアップに書き込む
bkup.write(buffer);
bkup.close
 
# ファイルを書き込みモードで開き直す
file = File.open(filename , "w");

#処理

cnt = 0
buffer.each {|line|
	cnt = cnt + 1
	#64行目から66行目をカットします。
	if !(64..66).include?(cnt)
	    #以前のバージョン2.7.0を今のバージョンに置き換えます。
	    file.puts line.gsub("2\.7\.0",ARGV[3])
	    #初期値では
		if cnt==94 
         heredoc= <<EOS
	      <!-- このlaunchers要素を新規に作る。idとmainClassは必須。 -->
	      <launchers>
	          <launcher>
	            <id>app</id>
	            <mainClass>PACKAGE.App</mainClass>
	            <!-- 以下、任意要素。 args と jvmArgs を指定できます。 -->
	            <!--
	            <args>
	              <arg>arg1</arg>
	            </args>
	            <jvmArgs>
	              <jvmArg>-Xmx128m</jvmArg>
	              <jvmArg>-Djava.library.path=...</jvmArg>
	            </jvmArgs>
	            -->
	          </launcher>
	      </launchers>
EOS
        	  file.puts heredoc.gsub("PACKAGE",ARGV[2])
         end
	end
}
file.close

実行

mkprj.bat problem99 problem 2.7.7 はなぜかコンパイルし終わると停止するので2度実行しています。

mkprj.bat problem99 problem 2.7.7
mkprj.bat problem99 problem 2.7.7
cd problem99
mvn scala:run

問09をMavenで実行させてみる。

実行

mvn scala:run
とりあえず、ソースをかけば、コンパイルから実行までやってくれるのがわかった。

さすがに、scalecを直接やるよりは遅い

クラスファイルの出力先

プロジェクトフォルダ\target\classes

インタプリタ形式でscalaを動かしたい場合

cd プロジェクトフォルダ\target\classes
scala

問11

単純な括弧、たとえば、(1,2)の要素を取得する

今まで、List型の要素取得方法を学んだ

scala> List(1,2)(1)
res12: Int = 2

しかし、同様の操作を上記のListを省いた形式にあてはめようとするとえらーになる。

scala> (1,2)(1)
<console>:7: error: scala.Tuple2.apply[Int, Int](1, 2) of type (Int, Int) does not take parameters

単なる括弧の要素を取得する方法

scala> (1,2)._1
res11: Int = 1

引数の型の数だけTupleクラスが用意されていて、APIを見る限り22個までは用意してあるらしい

問12

List.make(2,'a)はList('a,'a)

これまでの問題で定義してきたメソッドをあたかもListのビルトインメソッドのように扱う

問題文では、decode(List( (4, 'a), (1, 'b), (2, 'c), (2, 'a), (1, 'd), (4, 'e)))
であったが、これを

問13

spanの結果を多重代入する

val (packed, next) = l.span(_ == l.head)

問14

flatMapを使った方法を思いつくかという問題

[編集]

問15

List.make(n, _)をつかって要素を増やした記述

 def drop[T](n: Int, l: List[T]): List[T] = {
   def dropR(c: Int, curList: List[T]): List[T] = (c, curList) match {
     case (_, Nil) => Nil
     case (1, _ :: tail) => dropR(n, tail)
     case (_, e :: tail) => e :: dropR(c - 1, tail)
   }
   dropR(n, l)
 }

回答をみて気がつくのは、defの定義方法である。通常{}でくくるかとおもうが、 いきなり( )表現をつかっている。一つの配列からつくられる高階関数内に処理を書けるからだとおもう。

case文の使い方で気がつくことは、eとtailである、この2つは、どこにも定義や代入の記述がないことをみると、予約されている変数だろう。パターンマッチの書き方も()に応じて用意してあるのだろう。たしか()はTuple2だったはずだ。

[編集]

問16

この問題の回答例1番目で学べるのはカウンターを使って周期的にcase文を選択させていることが学べます。

zipWithIndex?

Rubyでのeach_with_indexみたいにインデックス番号付きに変換します。

例文

List(2, 3, 5).zipWithIndex.foreach(t => println(t._2 + ":" + t._1))
for ((d, i) <- List(2, 5, 6).zipWithIndex) {println(i + ":" + d)}
List(7, 3, 1).zipWithIndex.foreach{case (d, i) => println(i + ":" + d)}

filter

条件式に一致する要素を抽出します。

 def drop[T](n: Int, l: List[T]): List[T] = 
   l.zipWithIndex.filter(v => (v._2 + 1) % n != 0).map(_._1)
}

問17

splitAtというビルトインメソッドで分割する例

[編集]

問18

slice関数を使った回答が一番簡単な問題です。

def slice[T](start: Int, end: Int, l: List[T]): List[T] = l.slice(start, end)
[編集]

問19

dropとtakeを使って:::で連結することで、rotateを実現しています。

[編集]

問20

問17でも使った、spiltAtとmatchを使って解くと解ける問題

[編集]

問21

これも問17でも使った、spiltAtとmatchを使って解くと解ける問題

spiltAtを使ってからmatchを使うとpreとpostに分割されている。 そこで、

case (pre, post) => 

と、書きます。

問22

[編集]

組み込みの変数にRangeが用意されていますが、第二引数未満の整数のリストを作成するようです。

def range(start: Int, end: Int): List[Int] = List.range(start, end + 1)

問23

[編集]

乱数の使い方を知っていると解ける

(new util.Random).nextInt(l.length)

問24

[編集]

配列を生成するには下記のようにRangeを使うとよい

List.range(1, max + 1)

問25

[編集]

mutable array のupdateを使う

問26

[編集]

sublist@を使う

// P26 (**) Generate the combinations of K distinct objects chosen from the N
//          elements of a list.
//     In how many ways can a committee of 3 be chosen from a group of 12
//     people?  We all know that there are C(12,3) = 220 possibilities (C(N,K)
//     denotes the well-known binomial coefficient).  For pure mathematicians,
//     this result may be great.  But we want to really generate all the possibilities.
//
//     Example:
//     scala> combinations(3, List('a, 'b, 'c, 'd, 'e, 'f))
//     res0: List[List[Symbol]] = List(List('a, 'b, 'c), List('a, 'b, 'd), List('a, 'b, 'e), ...
object P26 {
 def combinations[T](n: Int, l: List[T]): List[List[T]] = {
   // flatMapSublists is like list.flatMap, but instead of passing each element
   // to the function, it passes successive sublists of L.
   def flatMapSublists[U](l: List[T], f: (List[T]) => List[U]): List[U] = 
     l match {
       case Nil => Nil
       case sublist@(_ :: tail) => f(sublist) ::: flatMapSublists(tail, f)
     }
   if (n == 0) List(Nil)
   else flatMapSublists(l, (sl) => combinations(n - 1, sl.tail).map((c) => sl.head :: c))
 }
}

難しい記述

問27

[編集]

すでに問題26で得ている解法というか、メソッドを使えば、この問題27を単純化できることに気がつく

object P27 {
 import P26.combinations

 def group3[T](l: List[T]): List[List[List[T]]] =
   for { a <- combinations(2, l)
        noA = l -- a
        b <- combinations(3, noA) }
   yield List(a, b, noA -- b)

 def group[T](ns: List[Int], l: List[T]): List[List[List[T]]] = ns match {
   case Nil => List(Nil)
   case n :: ns => combinations(n, l).flatMap(c => group(ns, l -- c).map(rest => c :: rest))
 }
}

配列の組み合わせによるfor文

scalaではfor文は配列の要素をループのカウントがわりに用いることができる。

yield文

for文のあとに記述することで、

for文のループ中の要素をyield文中のプログラムを実行することが可能です。

--表記

noA = l -- a

配列lから、配列aの要素を除いたモノを返す

問28

[編集]

slacaの配列のソートはsortメソッドが用意されています。

このsortメソッドに比較コードを書くことでsortが実施されます。

比較コードには、scala独特の _ で比較対象の要素を示すことが可能です。

問題bの方は、既に問題10で作成した、要素の長さの配列を得るメソッドを活用します。

既に作った関数をうまく使うことができるかどうかという問題でもあるわけですね。

問29

素数を扱う問題で

7.isPrime

と記述できるなんて、数値がオブジェクトでありなおかつ、そのメソッドを定義できるということですよね。

Scala言語がいかに洗練された言語かがわかります。

class S99Int(val start: Int) {
 def isPrime = primes.takeWhile(_ <= Math.sqrt(start)).forall(start % _ != 0)
}
object S99Int {
 def primes: Stream[Int] = Stream.cons(2, Stream.from(3, 2).filter(_.isPrime))
}

*1 4, 'a), (1, 'b), (2, 'c), (2, 'a), (1, 'd), (4, 'e
トップ   新規 一覧 単語検索 最終更新   ヘルプ   最終更新のRSS