Scalaスケーラブルプログラミング読書メモ2
クラスパラメータ
クラス名の直後に書くコンストラクタを基本コンストラクタと呼び,基本コンストラクタの引数をクラスパラメータという.
class Rational(n: Int, d: Int)
事前条件チェック
require
を使うと,引数に渡される値に制限を設けることができる.
class Rational(n: Int, d: Int){ require(d != 0) override def toString = n + "/" + d }
クラスにおけるvar
, val
の可視性
class Num(n: Int) // private class Num(val n: Int) // public, valなのでイミュータブル class Num(var n: Int) // public, varなのでミュータブル
補助コンストラクター
class Rational(n: Int, d: Int){ def this(n: Int) = this(n, 1) }
リテラル識別子
識別子とは,変数名・関数名・クラス名などに用いられるもの.リテラル識別子とは,バッククォートで囲まれた任意の文字列.リテラル識別子の考え方は,バッククォートにどんな文字列をいれてもランタイムに識別を受付させようというもの.
case class `Rational`(n: Int, d: Int){ require(d != 0) override def toString = n + "/" + d } val r = `Rational`(3, 4) println(r.`toString`)
for式
// yield val evens = for(n <- (1 to 10) if n % 2 == 0) yield n println(evens) // フィルタリング for(n <- (1 to 10) if n % 2 == 0){ println(n) } // 複数フィルター for(n <- (1 to 10) if n % 2 == 0 if 3 < n )println(n) // 入れ子,中間結果の束縛 for( file <- filesHere if file.getName.endsWith(".scala"); line <- fileLines(file) trimmed = line.trim if trimmed.matches(pattern) ) println(file + ": " + trimmed)
関数のプレースホルダー構文
// コンパイラーが十分な型情報を持っていない時の対策 val f = (_: Int) + (_: Int) println(f(3, 4))
その他
Scalaの型について(Scalaスケーラブルプログラミング読書メモ1)
型が重要な感じがしたのでメモ.
静的な型づけ
静的型付け言語であるScalaでは,ジェネリクスで型をパラメータ化したり,抽象型で型の詳細を隠蔽することができる.型が動的な言語(Python,Rubyとか)では,ジェネリクスや抽象型などの仕組みはなくとも,上記のようなことができる.そのため,Scalaでジェネリクスや抽象型を使用することは煩わしく感じられることもあるが,静的な型には様々なメリットが存在する.
静的な型付け言語では,プログラム実行より前に型が決まっている.プログラム実行前に型に関する情報がわかるため,次のようなメリットをもたらす.
検証可能性
静的な型システムはある種のランタイムエラーが存在しないことを証明できる.
例えば,次のようなコードの場合,コンパイル時にエラーが判明する.(IDEであれば,赤線を引いてくれる)
val convertToString = (num: Int) => num.toString() println(convertToString(true))
他にもプライベートなメソッドに外部からアクセスしていないかどうかや,関数に渡した引数の数が正しいかどうか等がプログラム実行前にわかる. (あからさまなエラーだと,動的型言語でもIDEが検出してくれるが,少し複雑なプログラムになると,検出できないはず)
一方で,動的な型付け言語では,プログラム実行前に検証しづらいため,しっかりテストを書く習慣があるらしいが, 最初から検証しやすい静的型付け言語を使えばよい気がする.
リファクタリング安全性
静的な言語で書かれたプログラムをリファクタリングする際,コンパイルが通るかどうかを確認することにより,変更内容が正しいかどうか検証することができる. (コンパイルが通ったからといって,プログラムにバグがないことを証明できるわけではないが...)
IDEのアシスト
静的型付け言語で書かれたプログラムをIDEで変数名やメソッド名を補完したり,リファクタリングをしたりする際,IDEのアシストが強力である.
新しい型を作れる
Scalaのクラスでは*
や-
などの演算子を実装できる.そのため,ネイティブサポートがあるかのように感じられる.
高水準な型システム
Javaで大文字が文字列に含まれているかどうかをチェックする際,ループで文字を1文字ずつ処理する必要があり,低水準なことを考慮しながらプログラムを書く必要がある.
// Java boolean nameHasUpperCase = false; for(int i = 0; i < name; ++i){ if(Character.isUpperCase(name.charAt(i))){ nameHasUpperCase = true; break; } }
対して,Scalaでは述語関数(条件を満たした場合に真、または偽を返す関数)で高水準にコードを書くことができる.ここでnameは紛れもなくJavaのStringクラスだが,必要に応じてStringOpsにラップ(暗黙変換)される.
// Scala val nameHasUpperCase = name.exists(_.isUpper)
この例では,本来低水準なもの(JavaのStringクラス)を高水準なもの(String + StringOps)として扱うことができ,さらにそれを組み込み型であるかのように扱えている.
コンパニオンオブジェクト
Scalaでオブジェクトを定義して,関数のようにオブジェクト名()
と呼び出すとapply
メソッドが自動的に呼び出される.
object Person{ def apply(name: String) { println(name) } } Person("ほげほげ") // "ほげほげ"と標準出力
これを利用すると,クライアントからnew演算子を使わずにPersonをインスタンス化することができる.
class Person(val name: String) object Person{ def apply(name: String): Person = { new Person(name) } } val p = Person("ふがふが") println(p.name) // "ふがふが"
ケースクラスならコンパニオンオブジェクトなしでインスタンス化できる.
case class Person(name: String) val p = Person("ふがふが") println(p.name) // "ふがふが"
Angulerのメモ
ファイル
ファイル名 | 説明 |
---|---|
app.component.ts | アプリで最初に呼び出されるコンポーネント.ルートコンポーネント. |
app.module.ts | 起動時に呼び出されるモジュール.Angulerの構成要素をまとめる器のようなもの. |
index.html | 最初のメインページ |
データバインディング
データバインディングの種類 | 記法 |
---|---|
補完 | [proerty] = "value" |
プロパティバインディング | {{...}} |
イベントバインディング | {event} = "handler" |
双方向バインディング | [(target)] = "value" |
属性バインディング | [attr.name] = "exp" |
クラスバインディング | [class.name] = "exp" |
スタイルバインディング | [style.name] = "exp" |
データバインディングの例
// プロパティバインディング @Component({ selector: 'my-app', template: '<img [src]="image" />' }) export class AppComponent{ image = "https://www.google.co.jp/images/branding/googlelogo/1x/googlelogo_color_272x92dp.png" }
<!-- 単位付きのスタイルプロパティ --> <!-- パーセントでフォントサイズを指定する例 --> <div [style.font-size.%]="size">hoge</div>
<!-- イベントバインディング --> <input type="button" (click)="show()" value="foo" /> // クリック時 <input type="button" (mouseenter)="show()" value="bar" /> // マウスポインターが要素に入った時 <input type="button" (input)="show($event)" value="foobar" /> // 入力内容が変更された時
// テンプレート参照変数 @Component({ selector: 'my-app', template: ` <input #txt id="txt" name="txt" type="text" (input)="show(txt.value)" /> <ul [innerHTML]="msg"></ul> ` }) export class AppComponent { ... show(input: string){ this.msg = += `<li>${input}</li>` } }
// テンプレート参照変数 template: ` <input #last type="text" (change)="foo()" /> <input #first type="text" (change)="bar()" /> <div>{{last.value}}{{first.value}}</div> // テンプレート内の他の要素から参照 `
<!-- キーイベントのフィルタリング --> <!-- `Enter`キーが押された時にだけイベントを発生させる --> <input ... (keyup.enter)="show($event)" />
<!-- 双方向バインディング --> <!-- ngModelディレクティブ --> <input id="name" name="name" type="text" [ngModel]="myName" />
「?.」演算子
<!-- `member`が空であるかどうかを確認し,空でない場合,nameにアクセス --> <div>{{member?.name}}</div>
パイプ
パイプとはテンプレート上に埋め込まれたデータを加工/整形するための仕組み. パイプには次のようなものが存在する.
lowercase
uppercase
titlecase
slice
date
number
percent
json
i18nPlural
i18nSelect
async
例
<!-- `price`は式,`currency`はパイプ,`'JPY'`はパイプのパラメータ. --> {{price | currency: 'JPY'}}
ディレクティブ
ディレクティブ | 説明 |
---|---|
ngIf | if文 |
ngSwitch | switch文 |
ngFor | for文 |
ngTemplateOutlet | 用意されたテンプレートの内容をインポート |
ngComponentOutlet | 用意されたコンポーネントの内容をインポート |
ngStyle | 要素にスタイルプロパティを付与 |
ngClass | クラスを付与 |
ngPlural | 数値に応じて出力を切り替える. |
ディレクティブの例
<!-- ディレクティブ名のプリフィックスに`*`が来ることに注意 --> <div *ngif="show"> <p>hoge hoge</p> </div>
<!-- `*ngStyle`ではなくて`[ngStyle]` --> <div> [ngStyle]="styles"> ... </div> ... export class AppComponent{ ... // 動的にスタイルを付与することが可能 get styles() { return { 'background-color': this.back ? '#f00' : '', 'color': this.fore ? '#fff' : '#000', } } }
フォーム
テンプレート駆動型のフォーム
- 検証属性が指定できる
required
minlength
maxlength
email
- 検証エラーかあるかどうかをチェック
フォーム名.invalid
フォームの状態検知
// 検証項目単位でエラーの有無をチェック 入力要素名.erros.検証型 // 検証型には`required`, `minlength`...などが当てはまる 入力要素名.valid 入力要素名.invalid フォーム名.valid フォーム名.invalid .pristine // 変更されていない .dirty // 更新された .touched // フォーカスが当たった .untouched // 1度もフォーカスが当たっていない
モデル駆動型のフォーム
app.module.ts
でReactiveFormsModule
を有効にする.template
に[formGroup]と
[formControl]`を記述する.- コンポーネントのクラスで
FormGroup
オブジェクトとFormControl
オブジェクトを生成する.
その他メモ
(ngModelChange)="exp"
でデータバインディング時の入力値を加工する- イベント発生時のマウス/キー情報を取得する
- イベントのバブリングをキャンセルする
React Nativeのメモ
アプリの初期化
# アプリの雛形を作成 expo init SampleApp # アプリを起動 cd SampleApp expo start # テストの追加 yarn add --dev jest # Redux yarn add redux yarn add react-redux # 画面遷移 yarn add react-navigation # flow yarn add --dev flow-bin@0.61.0 babel-preset-flow
package.json
"license": "UNLICENSED", "scripts": { ... "flow": "flow", "flow-stop": "flow stop” }
.flowconfig
[ignore] <PROJECT_ROOT>/node_modules/.* <PROJECT_ROOT>/libdefs.js [include] [libs] ./libdefs.js [options] all=true
var, let, constによる変数宣言
varは関数スコープ
letはブロックスコープ
constはブロックスコープでイミュータブル
コンポーネントのkey
コンポーネントがレンダリングされる際、コンポーネントはkeyという属性を保持している。 したがって、次のようにkeyを指定せずにコンポーネントをレンダリングしようとすると、warningが発生する。
<View> { ["1", "2", "3"].map((item) => { return (<Text>{"アイテム: " + item}</Text>) // warning })} </View>
keyの指定にはkeyExtractorという専用の属性が存在し、keyの指定にはこれを用いることができる。
keyExtractor={(item, index) => "hoge_" + item.index}
はじめてAkkaを学んだ感想
「Akka実践バイブル」の1~2章を読んだ感想について書きます。
Fluxと似ている
Akkaの第一印象は「Fluxと似ている」です。私の思ったことを図示すると、↓こんな感じになります。
React Nativeにaws-amplifyをインポートしたらSyntaxErrorでハマった
create-react-native-appでアプリを作成し、aws-amplifyをインポートし、テストすると、下記のようなエラーが発生します。
import Amplify from 'aws-amplify'; import aws_exports from './aws-exports';
● Test suite failed to run /???/???/node_modules/aws-amplify-react-native/dist/index.js:14 import { default as AmplifyCore, I18n } from 'aws-amplify'; ^^^^^^ SyntaxError: Unexpected token import > 1 | import React from 'react'; 2 | import { StyleSheet, Text, View } from 'react-native'; 3 | import Amplify, { withAuthenticator } from 'aws-amplify-react-native'; at ScriptTransformer._transformAndBuildScript (node_modules/jest-runtime/build/script_transformer.js:316:17) at Object.<anonymous> (App.js:1:884)||<
原因はnode_modules/aws-amplify-react-native/dist/index.js:14がトランスパイルされていないことです。
なので、トランスパイルするようにpackage.jsonに次のような記述をするとテストが通ります。
"jest": { "preset": "jest-expo", "transformIgnorePatterns": [] }
ただ、この設定だと余計なファイルも含めてすべてトランスパイルされるのでテストに時間がかかります。
なのでyarn testして1回トランスパイルしたあと、transformIgnorePatternsを削除するとテストが速く動きます。
"jest": { "preset": "jest-expo", }