2014-06-02
オリエンテーション
(目的の共有)
組み込みシステム概論
ソフトウェア開発手法
ハードウェア基礎 1
(デジタル編)
組み込みOS 1
(WEC)
プログラミング言語 1
(関数型言語) ハードウェア基礎 2
(アナログ編)
組み込みOS 2
(nxtOSEK)
プログラミング言語 2
(オブジェクト指向言語)
クラウド連携
組み込みOS 3
(Linux)
プログラミング言語 3
(GPU用DSL)
ソフトウェア設計とテスト
プロジェクト管理
プログラミング言語 4
(アセンブラとC言語)
:関連の向き
:スキルとして重点を置く項目
:重点項目のための基礎知識
:応用項目・付加スキル
オリエンテーション
(目的の共有)
組み込みシステム概論
ソフトウェア開発手法
ハードウェア基礎 1
(デジタル編)
組み込みOS 1
(WEC)
プログラミング言語 1
(関数型言語) ハードウェア基礎 2
(アナログ編)
組み込みOS 2
(nxtOSEK)
プログラミング言語 2
(オブジェクト指向言語)
クラウド連携
組み込みOS 3
(Linux)
プログラミング言語 3
(GPU用DSL)
ソフトウェア設計とテスト
プロジェクト管理
プログラミング言語 4
(アセンブラとC言語)
:関連の向き
:スキルとして重点を置く項目
:重点項目のための基礎知識
:応用項目・付加スキル
「プログラミング実技」を入れる?
:アプリケーション(C#)と
デバイスドライバ(C)
はじめに(なぜ OOPL を使っているか)
オブジェクト指向言語と関数型の違い
多相性について
プログラミング言語概論を振り返る
開発ツールが充実している
OS やミドルウェアの API の多くで採用さ
れている手続き型言語と、親和性が高い
オープンソースのソフトウェアの多くが
オブジェクト指向言語で書かれている
これらの前提/環境が変われば、
利用するプログラミング言語も変
わるでしょう。
見立ての違い:関数適用対メッセージ渡し
抽象データ型:データと操作の一体化
部分型多相によるプラグイン
継承による差分プログラミング
見立ての違い:関数適用対メッセージ渡し
抽象データ型:データと操作の一体化
部分型多相によるプラグイン
継承による差分プログラミング
読みやすさと書きやすさ
保守性、堅牢性
再利用性、拡張性 &読みやすさ
再利用性、拡張性
オブジェクト:関数型言語のクロージャ
局所的な変数を共有する関数のセット
メッセージの値でクロージャ内の関数を指定
(define (makeResistorObj resistorVal numBar)
(define resistor (makeResistorFromVal resistorVal numBar))
(lambda (msg)
(cond
((eq? msg 'val)
(resistor-value resistor)
)
((eq? msg 'code)
(resistor-colorCode resistor)
)
((eq? msg 'show)
(display "value: ")
(display (resistor-value resistor))
(display "ncolor code: ")
(showColorCode (resistor-colorCode resistor))
)
)
)
)
(define resistorObj (makeResistorObj 440 3))
(resistorObj ‘show)
複合データの操作を、型の一部として定
義する
複合データの実体生成も型の一部として定義
TASK(TaskMain)
{
// オブジェクト間のリンクを構築する
lineTracer.colorJudgement = &colorJudgement;
lineTracer.balanceRunner = &balanceRunner;
colorJudgement.lightSensor = &lightSensor;
balanceRunner.gyroSensor = &gyroSensor;
balanceRunner.leftMotor = &leftMotor;
balanceRunner.rightMotor = &rightMotor;
// 各オブジェクトを初期化する
LineTracer_init(&lineTracer);
ColorJudgement_init(&colorJudgement);
BalanceRunner_init(&balanceRunner);
LightSensor_init(&lightSensor, NXT_PORT_S3);
GyroSensor_init(&gyroSensor, NXT_PORT_S1);
Motor_init(&leftMotor, NXT_PORT_C);
Motor_init(&rightMotor, NXT_PORT_B);
// 4ms周期で、ライントレーサにトレース走行を依頼する
while(1)
{
LineTracer_trace(&lineTracer);
systick_wait_ms(4);
}
}
複合データの操作を、型の一部として定
義する
複合データの実体生成も型の一部として定義
TASK(TaskMain)
{
// オブジェクト間のリンクを構築する
lineTracer.colorJudgement = &colorJudgement;
lineTracer.balanceRunner = &balanceRunner;
colorJudgement.lightSensor = &lightSensor;
balanceRunner.gyroSensor = &gyroSensor;
balanceRunner.leftMotor = &leftMotor;
balanceRunner.rightMotor = &rightMotor;
// 各オブジェクトを初期化する
LineTracer_init(&lineTracer);
ColorJudgement_init(&colorJudgement);
BalanceRunner_init(&balanceRunner);
LightSensor_init(&lightSensor, NXT_PORT_S3);
GyroSensor_init(&gyroSensor, NXT_PORT_S1);
Motor_init(&leftMotor, NXT_PORT_C);
Motor_init(&rightMotor, NXT_PORT_B);
// 4ms周期で、ライントレーサにトレース走行を依頼する
while(1)
{
LineTracer_trace(&lineTracer);
systick_wait_ms(4);
}
}
TASK(TaskMain)
{
// オブジェクトの生成・初期化とオブジェクト間のリンク構築
Motor leftMotor(NXT_PORT_C);
Motor rightMotor(NXT_PORT_B);
GyroSensor gyroSensor(NXT_PORT_S1);
LightSensor lightSensor(NXT_PORT_S3);
BalanceRunner balanceRunner(&gyroSensor, &leftMotor, &rightMotor);
ColorJudgement colorJudgement(&lightSensor);
LineTracer lineTracer(&colorJudgement, &balanceRunner);
// 4ms周期で、ライントレーサにトレース走行を依頼する
while (true) {
lineTracer.trace();
::systick_wait_ms(4);
}
}
レコードは、複合データのフィールド値
の更新操作が固定
複数の値/フィールドを一度に変更するには
関数定義が必要
レコードのフィールド値を個別に更新すると、
不整合な状態が見えてしまう。
直角三角形の平行移動
type Point = { x : int; y : int; }
type RightTriangle = {
mutable vertex1 : Point;
mutable vertex2 : Point;
mutable vertex3 : Point;
}
let makeRightTriangle (leftBottom : Point) (height : int) (baseLen : int) =
{
vertex1 = leftBottom
vertex2 = { leftBottom with y = leftBottom.y + height }
vertex3 = { leftBottom with x = leftBottom.x + baseLen }
}
let rt1 = makeRightTriangle { x = 10; y = 0 } 30 40
let doSomething (rt : RightTriangle) =
...
// 20だけ右に並行移動
rt.vertex1 <- { rt.vertex1 with x = rt.vertex1.x + 20 } // ★
rt.vertex2 <- { rt.vertex2 with x = rt.vertex2.x + 20 }
rt.vertex3 <- { rt.vertex3 with x = rt.vertex3.x + 20 }
★の行を実行した時点では、rt は
直角三角形ではない!
型(複合データ)の性質を損なわない単位の
操作を、型に対応付ける役割
副作用要素(上書き代入; mutable)を
カプセル化
デバイスドライバの場合
USB のクラスドライバ
WEC の電源管理ドライバのクラス構成
例)「抵抗」の型を機能拡張する場合
type resistor = {
value : int32;
colorCode : string []
}
let showColorCode (r : resistor) =
... // ★
let sum (r1 : resistor) (r2 : resistor) : int32 =
r1.value + r2.value // ★★
定格電力(power limit)を指定して、印可可能な
最大電圧を計算できるようにしたい!
例)「抵抗」の型を機能拡張する場合
type resistor = {
value : int32;
colorCode : string []
}
let showColorCode (r : resistor) =
... // ★
let sum (r1 : resistor) (r2 : resistor) : int32 =
r1.value + r2.value // ★★
type resistorEx = {
value : int32;
colorCode : string []
powerLimit : int;
}
or
type resistorEx = {
base : resistor;
powerLimit : int;
}
let showColorCodeEx (r : resistorEx) =
...
let sum (r1 : resistor) (r2 : resistor) : int32 =
…
★や★★と全く同じコードを
書かなければいけない!
かといって、既存のコードを
直接書き換えたくない。
例)「抵抗」の型を機能拡張する場合
type resistor(v:int32, cc:string []) =
class
let valueV = v
let colorCodeV = cc
member this.value = vaueV;
member this.colorCode = colorCodeV;
member this.sum(r2 : resistor) : int32 =
this.value + r2.value
...
end
}
type resistorEx(v:int32, cc:string [], pl:int) =
class
inherit resistor(v, cc)
let powerLimitV = pl
member this.powerLimit = powerLimitV
end
resistorEx 型の値(オブジェクト;
クラスのインスタンス)に対して
は、value, colorCode, sum() を呼
び出せる。
F# の場合
> let mul x y = x * y;;
val mul : x:int -> y:int -> int
> let mul (x:'a) (y:'a) = x * y;;
val mul : x:int -> y:int -> int
> mul:('a -> 'a -> 'a) = (*);;
stdin(xx,xx): warning FS0064: This construct causes code to be less generic
than indicated by the type annotations. The type variable 'a has been
constrained to be type 'int'.
四則演算を行う関数を定義しようと
すると、int 型に型推論されてしま
い、総称関数を作れない!
F# の場合
こうすれば OK。
 let inline mul (x:'a) (y:'a) : 'a = x * y;;
val inline mul :
x: ^a -> y: ^a -> ^a when ^a : (static member ( * ) : ^a * ^a -> ^a)
> let muli:(int -> int -> int) = mul;;
val muli : (int -> int -> int)
> let mulf:(float -> float -> float) = mul;;
val mulf : (float -> float -> float)
> muli 3 5;;
val it : int = 15
> mulf 3. 5.;;
val it : float = 15.0
> mul 3 5;;
val it : int = 15
> mul 3. 5.;;
val it : float = 15.0
Haskell の場合
Prelude> :set +m
Prelude> data Point = P { x :: Float, y :: Float }
Prelude> let p1 = P { x = 3.0, y = 4.0 }
Prelude> let p2 = P { x = 5.0, y = 6.0 }
Prelude> p1 + p2
<interactive>:5:4:
No instance for (Num Point) arising from a use of `+'
Possible fix: add an instance declaration for (Num Point)
In the expression: p1 + p2
In an equation for `it': it = p1 + p2
Prelude> instance Num Point where (P x1 y1) + (P x2 y2) = (P (x1 + x2) (y1 + y2))
※ '*', 'abs', 'signum', 'fromInteger' が定義されていないという警告が出る。
Prelude> let p3 = p1 + p2
Prelude> x p3
8.0
Prelude> y p3
10.0
haskell.org の Wiki にある Polymorphism
の説明
http://www.haskell.org/haskellwiki/Polymorphism
OO 言語で使われる inclusion polymorphism と
subtyping についても言及
Wikipedia の関連ページ
Polymorphism (computer science)
http://en.wikipedia.org/wiki/Polymorphism_(computer_science)
Subtyping
http://en.wikipedia.org/wiki/Inclusion_polymorphism
SOLID (object-oriented design)
http://en.wikipedia.org/wiki/SOLID_(object-oriented_design)
ソフトウェアの品質要因
正確さ、頑丈さ
拡張性、再利用性
互換性
可搬性
使いやすさ
効率性
適時性、経済性、機能性
(「オブジェクト指向入門」第1章)
データ型
データの種類を明確に定義して扱う
型定義と型検査(型付け)
抽象化
詳しく見過ぎない(具象を削って汎用化)
権限移譲(「万能関数」≠ 抽象化)
抽象化は、責務を決める
ことでもある。
責務/役割の分担には、
契約の考え方が重要!
(データ)型とは
値や対象を分類・区別する性質
同じ型の値には同じ操作を適用できる
型の持つ性質は、操作で変わらない
例:整数(integer)の場合
integer 型を「自然に」扱え
るのは、数の体系が適切に
定められているからです!
(データ)型とは
値や対象を分類・区別する性質
同じ型の値には同じ操作を適用できる
型の持つ性質は、操作で変わらない
例:整数(integer)の場合
integer 型を「自然に」扱え
るのは、数の体系が適切に
定められているからです!
注意:数値の演算結果が数値に
ならない例外ケースも規定され
ています。
例: ゼロ除算, √-1, log(-1)
←JavaScript では NaN で扱う。
(NaN; Not a Number)
抽象化(と型)
データ構造と操作を結合:抽象データ型
データ構造+操作を単位とした責務の分担
データ構造を「値」として見れば・・・
データ構造+操作を、型と
して扱えるためには、この
一貫性が重要。
操作で性質が変わらないこと
:不変性
OOP言語(Eiffel)のアプローチ
事前条件・事後条件とクラス不変表明による、
抽象データ型の特性の担保
純粋関数型言語(Haskell)のアプローチ
純粋関数型言語としての特性
monad による副作用要素の隠蔽
評価戦略指定(最外最左が基本で最内を指定可能)
正しい「型」を作ることを意識
例)銀行口座の場合

2014年の社内新人教育テキスト #2(関数型言語からオブジェクト指向言語へ)