SlideShare a Scribd company logo
1 of 128
Download to read offline
Listの実装から学ぶ
Scala & 関数型プログラミング入門
1
自己紹介
羽田有輝同志社大学経済学部bizreach 新卒2年目学生時代はJavaを書いていたScalaは配属後初めて触った
2
アジェンダ
3
やることListの実装の前準備Scalaの特徴関数型プログラミングの特徴ScalaのList(collectionクラス)の特徴
Listの実装+ その際に使用するScalaの文法の説明
4
やらないこと環境構築の詳しい説明リストの実装で作成では触れないScalaの知識関数型の発展的内容(モナドの話とか)
5
Listの実装の前準備Scalaの特徴関数型プログラミングの特徴ScalaのList(collectionクラス)特徴
6
Scalaの特徴
JVMの環境で動くプログラミング言語である
全てがオブジェクトとして扱われるオブジェクト指向言語
上記に加え、関数型プログラミングの思想も取り入れているJavaよりも簡潔な表記が可能関数型、手続き型どちらの書き方も可能
7
Scala Official Page
https://www.scala-lang.org/
8
Javaよりも簡潔な表記が可能
9
このJavaのコードが
class MyClass {
private int index;
private String name;
public MyClass(int index, String name) {
this.index = index;
this.name = name;
}
}
10
Scalaだとこんな感じ
↓
Scalaだとこんなにスッキリかける
ボイラープレートが減っていい感じ
class MyClass (i: Int, n: String){
var index = i
var name = n
}
class MyClass (var index: Int, var name: String)
11
Java 7
Java 8
//名前 大文字 含 判定
boolean nameHasUpperCase = false;
for (int i = 0; i < name.length(); ++i) {
if (character.isUpperCase(name.charAt(i))){
nameHasUpperCase=true;
break;
}
}
boolean nameHasUpperCase = name.chars().anyMatch(
(intch) -> Character.isUpperCase((char)ch);
)
12
Scala
↓
↓
val nameHasUpperCase: Boolean = name.exists{ (char: Char) =>
char.isUpper
}
// 型推論 型宣言省略可
val nameHasUpperCase: Boolean = name.exists(char => char.isUpper)
// 関数内 一度 出 引数 _ 省略化
val nameHasUpperCase = name.exists(_.isUpper)
13
Scalaでは、
簡潔にコードを書けるように様々な工夫がなされている型推論によって、冗長な表記を省略する事ができる記述量を少なくする事により、本質的な部分に集中できる
14
静的型付けのメリット+ LL言語のよう
な書きやすさ
15
Listの実装の前準備Scalaの特徴関数型プログラミングの特徴ScalaのList(collectionクラス)特徴
16
関数型の特徴
関数を引数にとったり、変数に入れる事ができる(first class object)
一度入れたものは変更不可能(immutable)
副作用がない(参照透過性)
状態を持たない
17
参照透過性とは
同じ条件(引数)を与えれば必ず同じ結果(返り値)が得られる他のいかなる機能の結果にも影響を与えない関数を値に置き換えても、文脈が変わらない
18
手続き型でも参照透過性は再現する事は可能だが考える事が多いimmutableやfirst-class-objectは参照透過性をシンプルに表現できるScalaでは変数や後述するListを基本的にimmutableとして扱う
19
Scalaではどのように表現され
るのか
20
関数を引数に取れる
変数に代入できる
val nameHasUpperCase: Boolean = name.exists{char =>
char.isUpper
}
// char => char.isUpper 引数 Char 取 Boolean 返 関数
val f = (x: Int) => x * 2
// 変数 入 事 ( 場合 引数 型 省略 )
21
一度入れたものは変更不可能
scala> val x = 2
x: Int = 2
scala> x = x + 1
<console>:12: error: reassignment to val
x = x + 1
^
scala> val y = x + 1 // 新 値 返
y: Int = 3
scala> x // 元 変数 変更
res1: Int = 2
22
immutableの例
JavaのStringはimmutableである
副作用がある例
String hoge = "abcde"
String reverse = hoge.toUpperCase()
System.out.println(hoge) //abcde
List<Int> list = new ArrayList<Int>(1,2,3,4,5)
ShuffleList.suffle(list)
System.out.println(list) //25341
23
Scalaでは
JavaのStringのように基本的に新しく変更されたものを返すmutableにする事もできる
24
Listの実装の前準備Scalaの特徴関数型プログラミングの特徴ScalaのList(collectionクラス)特徴
25
Javaのリストの特徴
Javaは基本配列型のリストを使う(ArrayList)
要素を追加すると、参照先の値が変わる要素は後ろに追加される手続き型の言語はこのパターンが多い
26
JavaのList
scala> val javaList: java.util.List[Int]
= new java.util.ArrayList[Int]()
javaList: java.util.List[Int] = []
scala> javaList
res1: java.util.List[Int] = []
scala> javaList.add(1)
res2: Boolean = true
scala> javaList // java 変更可
res3: java.util.List[Int] = [1]
scala> javaList.add(2)
res4: Boolean = true
27
Scalaのリストの特徴
一方Scalaは基本単方向線形リスト追加する時に、新たなリストを返し、元データは変化しない要素は前に追加されるjavaにはない便利なメソッドがある関数型の言語はこのパターンが多い
28
ScalaのList
scala> val scalaList = Nil // Nil 空 表
scalaList: scala.collection.immutable.Nil.type = List()
scala> val intList = 1 :: Nil //:: 追加
intList: List[Int] = List(1)
scala> scalaList //元 変数 変化
res1: scala.collection.immutable.Nil.type = List()
scala> val intList2 = 2 :: intList // 要素 前 追加
intList2: List[Int] = List(2, 1)
29
JavaのListにはない便利なメソッド
2の倍数を2倍したリストを作成する時
// java
List<Int> list = new ArrayList<Int>(1,2,3,4);
List<Int> doubleList = new ArrayList<Int>();
for(Int num: list) {
if(num % 2 == 0){
doubleList.add(num * 2)
}
}
// scala
val doubleList = List(1,2,3,4,5).filter{x =>
x % 2 == 0
}.map(x => x * 2)
30
Listのメソッドについて
リストには関数を渡すメソッドが多くあるforループを使う必要がなくなるので、記述が短くなる新しいリストが返されるので、パイプ処理のようにメソッドを繋いでいける
31
collectionメソッドを使いこなせれば
非常に強力
32
初心者の自分が感じた疑問
確かにScalaは書きやすい工夫をしてくれているが、理解するのが難しい関数を引数に取るってどういう事?なにがいいのか?
immutableなのは便利なのはわかったけど、実際にコードでどうやって表現するの?
headとtailに別れててなにが嬉しいのか普通に後ろに足したほうが便利なのではcollectionメソッドに慣れない
33
自分がオススメする勉強法
タイトルにあるListの実装をしてみる事scala.collection.immutable.Listと同じ挙動をする物を作成する事社内のScala勉強会の題材として使われた
34
なぜオススメなのか
作成過程で、scalaの文法知識が多く出てくる関数型の特徴である、イミュータブルをどのように表現されているかイメージが付くListのデータ構造がわかるcollectionクラスの持つメソッドの理解が深まる
35
さっそく作ってみよう
36
ここからの流れ
実際にリストの実装を手順をこれから紹介しますそこで出て来るScalaの知識の説明をします実際に皆様にもコードを書いて頂いて、どう動くのかを見ていただきます
37
実行環境
実装例
https://scalafiddle.io/
https://scalafiddle.io/sf/zh5tvwu/7
38
Scalaを一回も触った事がない人には厳しいかもし
れないですが、
なんとなく、List実装がScala学ぶのに効果的だとい
う事がわかって
いただければ大丈夫です
39
List実装のルールの流れ
1. scala.collection.immutable.Listのデータ構造の再現
2. scala.collection.immutable.Listのメソッドの振る舞いを再現
40
Listとデータ構造の実装
41
Listとなるclassを作成
sealed abstract class MyList[+A]
case object MyNil extends MyList[Nothing]
case class MyCons[B](
head: B,
tail: MyList[B]
) extends MyList[B]
42
sealedとは?
同一ソースファイル内のクラスは継承できるが、別ファイルのクラスからは継承できなくなるこれを使う事で、同一ファイルに存在するサブクラス以外存在しない事を保証できるJavaでいうEnum(列挙型)を表現できる
43
MyList
MyNil(要素が一つもない)
MyCons(要素が一つ以上)
の2種類で表現できる
44
case classとは?
拡張されたclass
便利な機能やメソッドがある(出てきた時に紹介)
直積型を表現できる(javaでいうjava beans? 構造体?)
45
MyCons
head(一番最初の要素)
tail(それ以外の要素)
の2種類で表現できる
46
たった三行で直和型(列挙型× 直積型)を表現できる
※ 代数的データ型についてはこちらの記事が詳し
く書いてある
http://qiita.com/shigemk2/items/31f6bdbf4dfebb0b9ad
47
Listはこのように表現される
[] = MyNil
[1] = MyCons(1,MyNil)
[1,2] = MyCons(1,MyCons(2,MyNil))
[1,2,3] = MyCons(1,MyCons(2,MyCons(3,MyNil)))
48
objectとは?
Javaでいうsingletonクラスインスタンスが一つだけ生成される
// MyNil 何 状態 表現可能 為、 一 十分
case object MyNil extends MyList[Nothing]
49
型パラメータについて
JavaでいうGenericsの事AやBなど一文字の英数字で表現される[A]
インスタンスを生成する際に、型を指定する事ができる
50
型の継承関係
http://docs.scala-lang.org/tutorials/tour/unified-
types.html
51
NothingとはNothingとはすべてのクラスの基底クラス(サブクラス)
Nothing -> Int, Nothing -> String など
52
非変と共変
非変MyList[A]
MyList[String] のList[Any] のサブクラスではない
共変MyList[+A]
MyList[String] -> List[Any] のサブクラスである
53
非変のリストだと
scala> sealed abstract class MyList[+A]
defined class MyList
scala> case object MyNil extends MyList[Nothing]
defined object MyNil
scala> case class MyCons[B](
hd: B,
tl: MyList[B]) extends MyList[B]
defined class MyCons
scala> val list: MyList[Int] = MyNil
//空 表現
<console>:13: error: type mismatch;
found : MyNil.type
required: MyList[Int]
54
この三行だけでも色々学べる
sealed abstract class MyList[+A]
case object MyNil extends MyList[Nothing]
case class MyCons[B](
head: B,
tail: MyList[B]
) extends MyList[B]
55
headとtail,isEmptyを持たせる
sealed abstract class MyList[+A] {
val head: A
val tail: MyList[A]
def isEmpty: Boolean
}
case object MyNil extends MyList[Nothing] {
val head: Nothing =
throw new NoSuchElementException("head of empty MyList")
val tail: MyList[Nothing] =
throw new NoSuchElementException("tail of empty MyList")
def isEmpty = true
}
case class MyCons(head: A tail: MyList[A]) extends MyList[A] {
def isEmpty = false
56
case classの機能
case class MyCons(head: A tail: MyList[A]) extends MyList[A] {
// case class 場合、引数 物 宣言
def isEmpty = false
}
57
MyNilについて
本家のNilを真似て、headとtailが参照されようとすると、例外を投げるようにした
case object MyNil extends MyList[Nothing] {
val head: Nothing =
throw new NoSuchElementException("head of empty MyList")
val tail: MyList[Nothing] =
throw new NoSuchElementException("tail of empty MyList")
def isEmpty = true
}
58
MyListのインスタンスを作成
sealed abstract class MyList[+A] {...}
case object MyNil extends MyList[Nothing] {...}
case class MyCons[B](
head: B,
tail: MyList[B]
) extends MyList[B] {...}
object MyList {
def apply[A](x: A*): MyList[A] = {
if (x.isEmpty) MyNil
else MyCons(x.head, apply(x.tail: _*))
}
}
59
classと同名のObject
コンパニオンオブジェクトと呼ぶjavaでいうクラスメソッド(static method)はこちらに書くインスタンスメソッドはclassの方に書く
60
applyメソッドとは
主にインスタンスを生成する目的で使用する特別なメソッドMyList.apply() は、MyList() のようにメソッドを省略して呼べるつまりnewせずにインスタンスが呼び出せる
61
case classの機能その2
case class は暗黙的にコンパニオンオブジェクトが生成されるその際、applyも暗黙的に定義される
def apply[A](x: A*): MyList[A] = {
if (x.isEmpty) MyNil
// MyCons case class 為、
// 用意
else MyCons(x.head, apply(x.tail: _*))
}
// 暗黙的 定義
// object MyCons {
// def apply[A](head:A,tail:MyList[A]) =
new MyCons(head, tail)
//}
62
A*とは
可変長引数と呼ぶAの型のオブジェクトをコンマ区切りで要素をsequenceとして入れる事ができるMyList(1,2,3,4,5)とする事で引数にSeq(1,2,3,4,5)を渡す事ができる
//1,2,3,4,5 区切 入 x Seq[A] 変換
def apply[A](x: A*): MyList[A] = {
// x = Seq(1,2,3,4,5)
if (x.isEmpty) MyNil
// seq 引数 時 _* 使用
else MyCons(x.head, apply(x.tail: _*))
}
63
要素を追加できるようにする
sealed abstract class MyList[+A] {
val head: A
val tail: MyList[A]
def isEmpty: Boolean
// 要素追加 持
def ::[B >: A](x: B): MyList[B] = ???
}
64
:: (要素追加)
65
要素を追加するメソッドを作成
def ::[B >: A](x: B): MyList[B] = MyCons(x,this)
66
>: (下限境界)について
本来Any型からNothing型まですべての下限境界で指定したクラス自身とその親クラスだけ受け付ける下限より下のSubクラスは境界のクラスに変換される
67
例
Man -> Human -> Creature
scala> class Creature
defined class Creature
scala> class Human extends Creature
defined class Human
scala> class Man extends Human
defined class Man
68
scala> val list = MyList[Human]() //型 直接指定 事
list: MyList[Human] = MyNil
scala> val CreatureList = new Creature :: list
CreatureList: MyList[Creature]
= MyCons(Creature@5e9b30c4,MyNil)
scala> val HumanList = new Human :: list
HumanList: MyList[Human]
= MyCons(Human@27d684d6,MyNil)
// Human型 下 下限 Human型扱
scala> val ManList = new Man :: list
ManList: MyList[Human] = MyCons(Man@7670cb40MyNil)
69
メソッド名が:で終わる物について
scala> val a = List(1,2,3,4)
a: List[Int] = List(1, 2, 3, 4)
scala> a.::(5)
res0: List[Int] = List(5, 1, 2, 3, 4)
scala> a
res1: List[Int] = List(1, 2, 3, 4)
scala> 0 :: a // 呼 出
res2: List[Int] = List(0, 1, 2, 3, 4)
70
Listのデータ構造の完成
scala> val list = List(1,2,3,4,5) // 作成 際 、new
list: MyList[Int] = MyList(1, MyList(2, MyList(3, MyList(4, MyList
scala> list.head // 一番最初 要素 取得
res1: Int = 1
scala> list.tail // 以外 取得
res2: MyList[Int] = MyList(2, MyList(3, MyList(4, MyList(5, MyNil
scala> list // 副作用
res3: MyList[Int] = MyList(1, MyList(2, MyList(3, MyList(4, MyList
scala> val newList = 0 :: list // 追加
res4: MyList[Int] = MyList(0,MyList(1, MyList(2, MyList(3, MyList
71
collectionメソッドの実装
72
Listのメソッドについて
Listを始め、collectionクラスには様々なメソッドがあるその多くは他の関数型言語でも存在している関数型プログラミングで頻出のメソッドに絞って実装していく実際に振る舞いを真似てみる事で理解が深まる
73
インターフェースを定義
sealed abstract class MyList[+A] {
...
def reverse: MyList[A] = ???
def map[B](f: A => B): MyList[B] = ???
def flatMap[B](f: A => MyList[B]): MyList[B] = ???
def filter[B](f: A => Boolean): MyList[B] = ???
def foreach[B](f: A => Unit): Unit = ???
def foldLeft[B](z: B)(f: (B, A) => B): B = ???
def foldRight[B](z: B)(f: (A, B) => B): B = ???
74
reverse
75
目標
scala> val a = List(1,2,3,4)
//a: List[Int] = List(1, 2, 3, 4)
scala> a.reverse
//a: List[Int] = List(4, 3, 2, 1)
76
ダメな例(例えば)
def reverse: A = {
var currentList = this
var newList = MyNil // var 使 mutable 方
while(currentList.isEmpty){
var newList = currentList.head :: newList
var currentList = currentList.tail
}
newList
}
77
Q. varを使わずにどうやって計
算すればいいのか
-> どうやって計算の途中結果をもてばいいのか?
78
A. 再帰処理を使いましょう
79
関数型的な書き方
ある関数の中で、その関数を呼び出すと再帰になる処理とは関係ない変数(result,tmpなど)を使わなくてもいい記述もすっきりする
80
def reverse: MyList[A] = {
// target 短 acc 形成
// target 空MyNil 時 acc 返
def loop(target: MyList[A],acc: MyList[A] = MyNil) = {
target match {
case MyNil => acc
case MyCons(head,tail) => loop(tail,head ::acc)
}
}
loop(this)
}
81
match とは
パターンマッチと呼ばれ、javaのcase文のような物case classレベルでの分岐が容易にできるclassを構成する各要素を変数に束縛できる
82
クラスの要素分解について
case MyCons(h,t) とする事で、hは最初の要素、tはそれ以外という変数に束縛されるここではMyConsのunapplyメソッド(抽出メソッド)の戻り値に依存するMyCons(h,t) == MyCons.unapply(this)
83
case classの機能その3
applyと同じくunapplyも暗黙的に定義されるここを変更する事で、パターンマッチの時に使える変数を変更できる
object MyCons {
def unapply(c: MyCons[A]):Option[(A,MyList[A])] =
(c.head,c.tail)
}
84
Listが線形リストである理由
データをイミュータブルなままで変換していくには、再帰処理が不可欠頭とそれ以外というデータの持ち方は、再帰処理で使い易い
85
map
86
mapとは
Listの各要素に引数に指定した関数を各要素に適用した新しいListを作成する
87
目標
scala> val a = List(1,2,3,4)
//a: List[Int] = List(1, 2, 3, 4)
scala> a.map(x => x * 2)
res0: List[Int] = List(2, 4, 6, 8)
scala> a.map(_.toString) // 型 変更化
res1: List[String] = List("1", "2", "3", "4")
88
// 例
def map[B](f:A => B): MyList[B] = {
if(isEmpty) MyNil
else f(head) :: tail.map(f)
}
89
なぜ駄目のか
再帰を使っているが末尾再帰になっていない為関数が呼ばれ続ける為、リストの長さに比例してコールスタックが消費される長いリストになるとstackoverflowを起こす為、ループに変換する必要がある末尾再帰になっているとコンパイルが単なるループに変換してくれる(スタックセーフ)
90
mapメソッドを末尾再帰で作る
def map[B](f:A => B): MyList[B] = {
def loop(target: MyList[A],acc: MyList[B] = MyNil) = {
target Match {
case MyNil => acc
// 自分 呼 出 終了
case MyCons(head,tail) => loop(tail,f(head) :: acc)
}
}
loop(this)
}
91
リストが逆になるのでreverseメソッドを使う。
def map[B](f:A => B): MyList[B] = {
def loop(target: MyList[A],acc: MyList[B] = MyNil) = {
target match {
case MyNil => acc
case MyCons(head,tail) =>
loop(tail,f(head) :: acc)
}
}
loop(this).reverse
}
92
デフォルト値の指定
引数にデフォルト値を設定できる引数に何も指定されていない場合はデフォルト値を使用する
def loop(target: MyList[A],acc: MyList[B] = MyNil) = ...
//第2引数 時、acc MyNil
loop(this).reverse
93
filter
94
filterとは
Booleanを返す関数(述語関数)でTrueの要素だけ残すList(1,2,3,4,5,6).filter(x => x % 2 == 0) = List(2,4,6)
95
目標
scala> val list = List(1,2,3,4)
//a: List[Int] = List(1, 2, 3, 4)
scala> list.filter(x => x % 2 == 0)
res0: List[Int] = List(2,4)
96
実装はこんな感じ
def filter(f:A => Boolean): MyList[B] = {
def loop(target: MyList[A],acc: MyList[B] = MyNil) = {
target match {
case MyNil => acc
case MyCons(head,tail) if f(head) =>
loop(tail,head :: acc)
case _ => loop(tail,acc)
}
}
loop(this).reverse
}
97
パターンガード
パターンマッチした値に対して、ifを定義して分岐させる事ができる束縛した変数も使用できる一致したcase class に対して、更に絞り込みをかけたいときに便利
98
flatMap
99
flatMapとは
ネストしたListを一つのListにしてくれるList(List(1,2,3),List(4,5),List(6)) => List(1,2,3,4,5,6)
flatten + map
100
目標
scala> val list = List(1,2,3,4)
//a: List[Int] = List(1, 2, 3, 4)
scala> list.map(x => List(x,x))
res0: List[Int] = List(List(1,1),List(2,2,),List(3,3),List(4,4))
scala> list.flatMap(x => List(x,x))
res1: List[Int] = List(1,1,2,2,3,3,4,4)
101
要素を結合するメソッドが必要
102
:::(List結合)
103
要素を追加するメソッドを作成
def :::[B >: A](l: MyList[B]): MyList[B] = {
def loop(a1: MyList[B], a2: MyList[B]) = {
a1 match {
case MyNil => a2
case MyCons(h,t) => MyCons(h, loop(t, a2))
}
}
loop(l,this)
}
104
実装はこんな感じ
def flatMap(f:A => MyList[A]): MyList[B] = {
def loop(target: MyList[A],acc: MyList[B] = MyNil)= {
target match {
case MyNil => acc
case MyCons(head,tail) =>
loop(tail,f(head) ::: acc)
}
}
loop(this).reverse
}
105
foreach
106
foreachとは
返り値がない(副作用がある)物に対して用いるUnit(返す物はないという意味のもの)を返すvoidとは違う
107
目標
scala> val a = List(1,2,3,4)
//a: List[Int] = List(1, 2, 3, 4)
scala> a.foreach(x => println(x))
1
2
3
4
108
実装はこんな感じ
def foreach(f: A => Unit): Unit = {
this match {
case MyNil => () // Unit () 表
case MyCons(head, tail) => {
f(head)
t.foreach(f)
}
}
}
109
foldLeft
110
foldLeftとは
fold(畳み込み) + Left(一番前から)
Listを前の要素から順に関数を適用するaccに蓄積されていく再帰の原理とほとんど同じ
111
目標
scala> val list = List(1,2,3,4)
// list: List[Int] = List(1, 2, 3, 4)
// acc 関数 結果 蓄積
scala> list.foldLeft("0"){ (acc,x) =>
| acc + x.toString
| }
res0: String = "01234"
112
実装はこんな感じ
def foldLeft[B](z: B)(f: (B, A) => B): B = {
def loop(l: List[A], acc: B)(f: (B, A) => B) =
l match {
case Nil => acc
case Cons(h,t) => loop(t, f(acc,h))(f)
}
loop(this,z)(f)
}
113
foldLeftでいままでのメソッドを書き
換え可能
def map[B](f: A => B): MyList[B]
= foldLeft[MyList[B]](MyNil) { (acc, xs) =>
f(xs) :: acc
}.reverse
114
foldRight
115
foldRight
fold(畳み込み) + Left(一番後ろから)
foldLeftとほぼ同じだが、accが第二引数の方になるListを後ろの要素から順に関数を適用する難易度高め
116
目標
scala> val list = List(1,2,3,4)
// list: List[Int] = List(1, 2, 3, 4)
scala> list.foldRight("5"){ (x,acc) =>
| acc + x.toString
| }
res0: String = "54321"
117
実装はこんな感じ
def foldRight[B](z: B)(f: (A, B) => B): B = {
def loop(xs: MyList[A], acc: B => B = identity) = {
xs match {
case MyNil => acc
case MyCons(h,t) => loop(t,x => acc(f(h,x)))
}
}
loop(this)(z)
}
118
関数は引数が決まるまで評価されない
内側から実行される-> 右から関数適用
List(1,2,3,4,5) 場合
List(1,2,3,4,5).foldRight(0)((x,acc) => x + acc)
(1,(2,3,4,5)) => loop((2,3,4,5), x => f(1,x))
(2,(3,4,5) => loop((3,4,5), x => f(1,f(2,x)))
(3,(4,5) => loop((4,5), x => f(1,f(2,f(3,f(4,f(5,x)))))
119
最低限の機能のListが完成!
scala> val a = MyList(1,2,3,4,5)
a: MyList[Int] = MyCons(1,MyCons(2,MyCons(3,MyCons(4,MyCons(5,MyNil
scala> a.map(x => x * 2)
res1: MyList[Int] = MyCons(2,MyCons(4,MyCons(6,MyCons(8,MyCons(10
scala> a.filter(x => x % 2 == 0)
res2: MyList[Int] = MyCons(2,MyCons(4,MyNil))
scala> a.flatMap(x => x :: x :: MyNil)
res3: MyList[Int] = MyCons(1,MyCons(1,MyCons(2,MyCons(2,MyCons(3
scala> a.foreach(println)
1
2
3
120
ちなみに
実際のscala.collection.immutable.Listでは、末尾再帰を用いず、手続き型で書いている手続き型でやっている事が関数型のインターフェースでラッピングされている
final override def map[B, That](f: A => B)(implicit bf: CanBuildFrom[List[A], B,
.
.
val h = new ::[B](f(head), Nil)
var t: ::[B] = h
var rest = tail
while (rest ne Nil) {
val nx = new ::(f(rest.head), Nil)
t.tl = nx
t = nx
rest = rest.tail
}
.
.
}
121
メソッドはたくさんある
Scalaコレクションメソッド
122
目指せ全実装!
123
まとめ
Listの実装は、scalaにあってjavaにはない機能に沢山触れる事ができる更に、関数型の基礎的な部分を学習できるscalaをある程度理解してきたタイミングでこれをやると効果的
124
オススメの参考書
125
Scalaの作者が書いた入門書の訳本まずこちらをある程度やってからList実装をやると効果的
関数型プログラミングについて、Scalaで実装していきながら学べるこちらでもListの実装するエクサイズが最初の方で出て来る難易度高め
スケーラブルプログラミング
fp in scala
126
おまけhttps://scalafiddle.io/sf/lbqKNT6/11
127
ご静聴ありがとうございました
128

More Related Content

More from dcubeio

Apiドキュメンテーションツールを使いこなす【api blueprint編】
Apiドキュメンテーションツールを使いこなす【api blueprint編】Apiドキュメンテーションツールを使いこなす【api blueprint編】
Apiドキュメンテーションツールを使いこなす【api blueprint編】dcubeio
 
春の脆弱性祭り 2017/06/13
春の脆弱性祭り 2017/06/13春の脆弱性祭り 2017/06/13
春の脆弱性祭り 2017/06/13dcubeio
 
DynamoDBを導入した話
DynamoDBを導入した話DynamoDBを導入した話
DynamoDBを導入した話dcubeio
 
Play2 scalaを2年やって学んだこと
Play2 scalaを2年やって学んだことPlay2 scalaを2年やって学んだこと
Play2 scalaを2年やって学んだことdcubeio
 
すごーい!APIドキュメントを更新するだけでAPIが自動テストできちゃう!たのしー!
すごーい!APIドキュメントを更新するだけでAPIが自動テストできちゃう!たのしー! すごーい!APIドキュメントを更新するだけでAPIが自動テストできちゃう!たのしー!
すごーい!APIドキュメントを更新するだけでAPIが自動テストできちゃう!たのしー! dcubeio
 
20170329 D3 DBAが夜間メンテをしなくなった日 発表資料
20170329 D3 DBAが夜間メンテをしなくなった日 発表資料20170329 D3 DBAが夜間メンテをしなくなった日 発表資料
20170329 D3 DBAが夜間メンテをしなくなった日 発表資料dcubeio
 
Bitcoin x Slack でマイクロペイメントを実現! 〜生活の必要上割り勘botを作るまで〜
Bitcoin x Slack でマイクロペイメントを実現! 〜生活の必要上割り勘botを作るまで〜Bitcoin x Slack でマイクロペイメントを実現! 〜生活の必要上割り勘botを作るまで〜
Bitcoin x Slack でマイクロペイメントを実現! 〜生活の必要上割り勘botを作るまで〜dcubeio
 
【freee】プロダクトマネージャーの仕事と魅力
【freee】プロダクトマネージャーの仕事と魅力【freee】プロダクトマネージャーの仕事と魅力
【freee】プロダクトマネージャーの仕事と魅力dcubeio
 
【ビズリーチ】プロダクトマネージャーの仕事と魅力
【ビズリーチ】プロダクトマネージャーの仕事と魅力【ビズリーチ】プロダクトマネージャーの仕事と魅力
【ビズリーチ】プロダクトマネージャーの仕事と魅力dcubeio
 
Python × Herokuで作る 雑談slack bot
Python × Herokuで作る 雑談slack botPython × Herokuで作る 雑談slack bot
Python × Herokuで作る 雑談slack botdcubeio
 
HR Tech x 機械学習 導入事例紹介
HR Tech x 機械学習 導入事例紹介HR Tech x 機械学習 導入事例紹介
HR Tech x 機械学習 導入事例紹介dcubeio
 
Scalaマクロ入門 bizr20170217
Scalaマクロ入門 bizr20170217 Scalaマクロ入門 bizr20170217
Scalaマクロ入門 bizr20170217 dcubeio
 
機械学習を支えるX86 64の拡張命令セットを読む会 20170212
機械学習を支えるX86 64の拡張命令セットを読む会 20170212機械学習を支えるX86 64の拡張命令セットを読む会 20170212
機械学習を支えるX86 64の拡張命令セットを読む会 20170212dcubeio
 
簡単、クレカ決済! PAY.JPを使ったクレカ決済の仕組み・開発運用時の考慮点について
簡単、クレカ決済! PAY.JPを使ったクレカ決済の仕組み・開発運用時の考慮点について簡単、クレカ決済! PAY.JPを使ったクレカ決済の仕組み・開発運用時の考慮点について
簡単、クレカ決済! PAY.JPを使ったクレカ決済の仕組み・開発運用時の考慮点についてdcubeio
 
Ansibleで始めるインフラ構築自動化
Ansibleで始めるインフラ構築自動化Ansibleで始めるインフラ構築自動化
Ansibleで始めるインフラ構築自動化dcubeio
 
はじめてのAws lambda
はじめてのAws lambdaはじめてのAws lambda
はじめてのAws lambdadcubeio
 
覚えて帰ろうJavaデザインパターン
覚えて帰ろうJavaデザインパターン覚えて帰ろうJavaデザインパターン
覚えて帰ろうJavaデザインパターンdcubeio
 
React NativeでTwitterクライアントを作ってみよう
React NativeでTwitterクライアントを作ってみようReact NativeでTwitterクライアントを作ってみよう
React NativeでTwitterクライアントを作ってみようdcubeio
 
React Native GUIDE
React Native GUIDEReact Native GUIDE
React Native GUIDEdcubeio
 
【D3 公開用】ドメイン駆動設計とscala 〜既存プロジェクトへの適用〜
【D3 公開用】ドメイン駆動設計とscala 〜既存プロジェクトへの適用〜 【D3 公開用】ドメイン駆動設計とscala 〜既存プロジェクトへの適用〜
【D3 公開用】ドメイン駆動設計とscala 〜既存プロジェクトへの適用〜 dcubeio
 

More from dcubeio (20)

Apiドキュメンテーションツールを使いこなす【api blueprint編】
Apiドキュメンテーションツールを使いこなす【api blueprint編】Apiドキュメンテーションツールを使いこなす【api blueprint編】
Apiドキュメンテーションツールを使いこなす【api blueprint編】
 
春の脆弱性祭り 2017/06/13
春の脆弱性祭り 2017/06/13春の脆弱性祭り 2017/06/13
春の脆弱性祭り 2017/06/13
 
DynamoDBを導入した話
DynamoDBを導入した話DynamoDBを導入した話
DynamoDBを導入した話
 
Play2 scalaを2年やって学んだこと
Play2 scalaを2年やって学んだことPlay2 scalaを2年やって学んだこと
Play2 scalaを2年やって学んだこと
 
すごーい!APIドキュメントを更新するだけでAPIが自動テストできちゃう!たのしー!
すごーい!APIドキュメントを更新するだけでAPIが自動テストできちゃう!たのしー! すごーい!APIドキュメントを更新するだけでAPIが自動テストできちゃう!たのしー!
すごーい!APIドキュメントを更新するだけでAPIが自動テストできちゃう!たのしー!
 
20170329 D3 DBAが夜間メンテをしなくなった日 発表資料
20170329 D3 DBAが夜間メンテをしなくなった日 発表資料20170329 D3 DBAが夜間メンテをしなくなった日 発表資料
20170329 D3 DBAが夜間メンテをしなくなった日 発表資料
 
Bitcoin x Slack でマイクロペイメントを実現! 〜生活の必要上割り勘botを作るまで〜
Bitcoin x Slack でマイクロペイメントを実現! 〜生活の必要上割り勘botを作るまで〜Bitcoin x Slack でマイクロペイメントを実現! 〜生活の必要上割り勘botを作るまで〜
Bitcoin x Slack でマイクロペイメントを実現! 〜生活の必要上割り勘botを作るまで〜
 
【freee】プロダクトマネージャーの仕事と魅力
【freee】プロダクトマネージャーの仕事と魅力【freee】プロダクトマネージャーの仕事と魅力
【freee】プロダクトマネージャーの仕事と魅力
 
【ビズリーチ】プロダクトマネージャーの仕事と魅力
【ビズリーチ】プロダクトマネージャーの仕事と魅力【ビズリーチ】プロダクトマネージャーの仕事と魅力
【ビズリーチ】プロダクトマネージャーの仕事と魅力
 
Python × Herokuで作る 雑談slack bot
Python × Herokuで作る 雑談slack botPython × Herokuで作る 雑談slack bot
Python × Herokuで作る 雑談slack bot
 
HR Tech x 機械学習 導入事例紹介
HR Tech x 機械学習 導入事例紹介HR Tech x 機械学習 導入事例紹介
HR Tech x 機械学習 導入事例紹介
 
Scalaマクロ入門 bizr20170217
Scalaマクロ入門 bizr20170217 Scalaマクロ入門 bizr20170217
Scalaマクロ入門 bizr20170217
 
機械学習を支えるX86 64の拡張命令セットを読む会 20170212
機械学習を支えるX86 64の拡張命令セットを読む会 20170212機械学習を支えるX86 64の拡張命令セットを読む会 20170212
機械学習を支えるX86 64の拡張命令セットを読む会 20170212
 
簡単、クレカ決済! PAY.JPを使ったクレカ決済の仕組み・開発運用時の考慮点について
簡単、クレカ決済! PAY.JPを使ったクレカ決済の仕組み・開発運用時の考慮点について簡単、クレカ決済! PAY.JPを使ったクレカ決済の仕組み・開発運用時の考慮点について
簡単、クレカ決済! PAY.JPを使ったクレカ決済の仕組み・開発運用時の考慮点について
 
Ansibleで始めるインフラ構築自動化
Ansibleで始めるインフラ構築自動化Ansibleで始めるインフラ構築自動化
Ansibleで始めるインフラ構築自動化
 
はじめてのAws lambda
はじめてのAws lambdaはじめてのAws lambda
はじめてのAws lambda
 
覚えて帰ろうJavaデザインパターン
覚えて帰ろうJavaデザインパターン覚えて帰ろうJavaデザインパターン
覚えて帰ろうJavaデザインパターン
 
React NativeでTwitterクライアントを作ってみよう
React NativeでTwitterクライアントを作ってみようReact NativeでTwitterクライアントを作ってみよう
React NativeでTwitterクライアントを作ってみよう
 
React Native GUIDE
React Native GUIDEReact Native GUIDE
React Native GUIDE
 
【D3 公開用】ドメイン駆動設計とscala 〜既存プロジェクトへの適用〜
【D3 公開用】ドメイン駆動設計とscala 〜既存プロジェクトへの適用〜 【D3 公開用】ドメイン駆動設計とscala 〜既存プロジェクトへの適用〜
【D3 公開用】ドメイン駆動設計とscala 〜既存プロジェクトへの適用〜
 

Listの実装から学ぶScala & 関数型プログラミング入門