WSL2でhello-minikubeできるようになるまでが長かったのでメモしておく。
続きを読むHyper-VにLubuntuをインストールしてDockerをセットアップする
Hyper-VにManjaro Linuxをインストールするのが大変だったのでメモ
2020.08.20 追記
今となっては素直にWSL2をインストールしたほうがあらゆる面で有利です。
プライベートPCに開発環境を作るに際して、
- 慣れているLinuxのエコシステムを使いたい(bashとか、power shellは好きじゃない)
- Macは開発環境としては良いが性能に比べ費用が割高
- Win上に直接開発環境を作りたくない、仮想化したい
という、わがまま要件を満たすため、スーパーバイザ型の仮想環境であるHyper-V上に、最近トレンドなArch系ディストロのManjaro Linuxを動かすことにした。
実際インストールしようとしたら結構わからなかったので、備忘メモを残しておく。
続きを読む開発に役立つWebツール集
モックしたい
単純なHTTPサーバがほしい
GETやPOSTなどのHTTPリクエストをオウム返ししてくれるサービス。
通信部分のアーキテクチャ部分を作るときや、 通信を仮組みしてフロントエンドやモバイルアプリを作るときに便利。 dockerイメージもあるので、カスタマイズしてローカルやECSなどに建てるのも簡単。
ダミーの画像を生成したい
URLにパラメータを渡すとそれにあった画像を生成してくれるサービス。
生成された画像はURLでアクセスが可能なため、 フロントエンドを仮組みしているときなど、当てはめる画像を探すのが面倒なときに ここで生成した画像を当て込むことができる。
ちょっと確認したい
正規表現をテストしたい
正規表現のパターンを任意の文字列でテストできるサービス。
PHPやJavaScriptなど、さまざまな言語の正規表現を検証できる。 単純なマッチングだけでなく、グルーピングでどんな値を 後方参照できるかなども確認できる。
JSONのフォーマット修正と構文チェックをしたい
JSONをフォーマットできるサービス。
JSONを返すAPIを提供しているサービスなどで、 データ量削減のために改行やインデントを消していて見にくい場合がある。 このサービスで整形を行うことができる。
あるエポック秒が指し示す時間を知りたい/ある時間のエポック秒が知りたい
エポック秒と時間の相互変換を行うサービス。
出力がエポック秒になっている場合、それがいつなのかわかりづらいが、 このサービスで可読性の高い時間表記に変更することができる。
データ変換・生成したい
URLエンコード/デコードしたい
URLをエンコード/デコードできるサービス。
Shift_JISやUTF-8などでエンコードされた文字が含まれるURLをデコードしたり、 その逆を行うことができる。
CSVをJSONにしたい
集計データなどはCSVになっていることが多いが、 プログラムではCSVよりJSONをパースするほうが手軽なことが多いため、 JSONに変換しておくと取り扱いがしやすい。
JSON Web Tokenのパースをしたい
JWTの文字列からペイロードを確認したり、ペイロードからJWTを生成できるサービス。
認証にJWTを使用している場合に、ペイロードが正しいか確認する場合に便利。
JSON Schemeを書きたい
JSON Schemaを編集できるサービス。
ちょっとPlaygroundで遊びたい
Scalaを試したい
Dartを試したい
Scalaの等価性
ScalaとJavaの違い
両言語の取扱いの違いについて下記にまとめる。
Scala | Java | |
---|---|---|
値の比較 | == |
== |
オブジェクトの比較(値等価) | == |
equals |
オブジェクトの比較(参照等価) | eq |
== |
オブジェクトの比較(ユーザ定義等価) | == (override equals ) |
override equals |
等価性実装のポイント
class A(val value: String) { var mutatedValue: String /* 3. */ override def equals(x: Any /* 1. */) = x match { case xx: A => value == xx.value case _ => false } override def hashCode = value.## /* 2. */ }
Any#equals
をオーバーライドする- 引数の型はクラスの型ではなく
Any
になる
- 引数の型はクラスの型ではなく
hashCode
は常にequals
で使っている値をすべて使って常に同じ計算結果となるようにする##
で簡単にハッシュ値を作れる
- ミュータブルなプロパティを
equals
に使用してはいけない - 下記条件を満たすようにする
x.equals(x) == true // x is not null
x.equals(y) == y.equals(x) // x, y is not null
when x.equals(y) == y.equals(z), x.equals(z) == true // x, y, z is not null
x.equals(null) == false // x is not null
- 冪等である
継承関係にあるクラスでのポイント
継承関係にあるクラスでの等価性は、基本的に厳格にしたほうが良い場合が多い。
例えば、下記のようなText
とAttributedText
のクラスでは、比較結果がtrue
になるパターンをあれこれ考えるより、相互に比較結果がfalse
になるように実装したほうが比較時の違和感が少ない。
class Text(val value: String) { override def equals(x: Any) = x match { case xx: Text => canEqual(x) && value == xx.value case _ => false } override def hashCode = value.## def canEqual(x: Any) = x.isInstanceOf[Text] // 一部の等価性を比較したい場合は`==`ではなく独自にメソッドを実装したほうがわかりやすい def equalsText(x: Text) = value == x.value } class AttributedText(override val value: String, val attributes: Map[String, Any]) extends Text(value) { override def equals(x: Any) = x match { case xx: AttributedText => canEqual(x) && super.equals(x) && attributes == xx.attributes case _ => false } override def hashCode = (value, attributes).## override def canEqual(x: Any) = x.isInstanceOf[AttributedText] }
上記のようにcanEqual
を実装し、インスタンス判定を条件に加えることで実現できる。
また、こうしておくと、無名サブクラスでも正しく判定されるようになる。
val text = new Text("test") val anotherText = new Text("sample") { override val value: String = "test" } println(text == anotherText) // ==> true
Scalaの変位パラメータ
前提
前提として、計数可能な性質を表すCountable
を定義する。
Countable
は説明と数量を保持する。
trait Countable { val description: String def number: Int }
ここでは、上記の性質をテーブルゲームに適用し、具体化したものとして、将棋の駒数、麻雀牌の数をモデリングしてみる。
abstract class TableGame extends Countable { val players: Int } case object Shogi extends TableGame { override val description = "将棋の駒数" override val number = 40 override val players = 2 } case object Mahjong extends TableGame { override val description = "麻雀牌の数" override val number = 136 override val players = 4 }
上限境界で与えられる型を限定する
Countable
の数量が最大であることを表す型Max
を定義してみる。
Max
は型パラメータT
を持っており、最大値を持つオブジェクトをvalue
として保持する。
また、与えられた値T
を自身と比較し、より大きい値を持つMax
を返却するメソッドmax
も実装する。
case class Max[T](initial: T) { val value = initial def max(x: T): Max[T] = if (x.number > value.number) Max(x) else this }
ただし、このままだとT
がCountable
を継承しない型をとれるため、max
でコンパイルエラーとなってしまう。
value number is not a member of type parameter T
そこで、T
がCountable
の性質をもつことを示すため、上限境界を設定する。
- case class Max[T](initial: T) { + case class Max[T <: Countable](initial: T) { // <: が上限境界の指定 val value = initial def max(x: T): Max[T] = if (x.number > value.number) Max(x) else this }
こうすることで、T
はCountable
のサブクラスのインスタンスまたはミックスインされたオブジェクトに限定することができるため、number
が参照できるようになる。
次のコードを実行すると結果が表示される。
val max = Max(Shogi).max(Shogi) println(s"${max.value.description}は${max.value.number}です") // ==> "将棋の駒数は40です。"
max
をより有用な処理にするために
先ほどの例では、同じものを比較しており、意味を感じられるものではなかった。 では次のコードを実行するとどうなるだろうか。
val max = Max(Shogi).max(Mahjong) println(s"${max.value.description}は${max.value.number}です")
少しは意味がありそうなコードになったが、実際に実行しようとするとコンパイルエラーとなる。
type mismatch;
found : Mahjong.type
required: Shogi.type
Max(Shogi)
のインスタンス型はMax[Shogi]
なので、max
が受け取る引数の型も、返却するMax
の型パラメータもShogi
を要求しているのである。これに対応するためにはCountable
を取り扱うようにすれば良い。
case class Max[T <: Countable](initial: T) { val value = initial - def max(x: T): Max[T] = if (x.number > value.number) Max(x) else this + def max(x: Countable): Max[Countable] = if (x.number > value.number) Max(x) else this }
しかし、依然としてコンパイルエラーとなる。
type mismatch;
found : Max[T]
required: Max[Countable]
Note: T <: Countable, but class Max is invariant in type T.
You may wish to define T as +T instead. (SLS 4.5)
これは、max
の定義として、Max[Countable]
を返却する宣言なのにMax[T]
を返却しているためである。
非変(invariant)と共変(co-variant)
このエラーに対処するためには非変と共変について理解しておく必要がある。
B extends A
であるとき、C[T]
についてC[B] extends C[A]
を満たすときのT
を共変という。
一方C[B]
とC[A]
をそれぞれ独立した型として扱うときのT
を非変という。
Scalaでは型パラメータは基本的に非変である。そのためエラーとなったのである。
ところで、ここでMax[T]
を返すことに何か問題はあるだろうか。
T
については下限境界を設定しているため、Countable
の性質を備えていることが自明であるため、Max[T]
がMax[Countable]
のサブ型とみて問題はないだろう。
そのため、ここでは型パラメータを共変に変更することでMax[T] extends Max[Countable]
であることを明示する。そうすれば、Max[Countable]
を要求する場面で、より具体的なMax[T]
を返却することができるようになる。
- case class Max[T <: Countable](initial: T) { + case class Max[+T <: Countable](initial: T) { // + が共変の指定 val value = initial def max(x: Countable): Max[Countable] = if (x.number > value.number) Max(x) else this }
これで、max
の結果は常にMax[Countable]
として得られるのである。
下限境界を使ってmax
をもっと便利に
maxにはもう少し改善の余地がある。
val max = Max(Shogi).max(Mahjong)
上記の変数max
の型はどうなるだろうか。
Shogi
とMahjong
しか渡していないため、結果はMax[Shogi]
かMax[Mahjong]
のどちらかにしかならないはずである。
つまり、Max[TableGame]
であることは確実である。
ところが、実際にはMax[Countable]
になってしまう。
いつでもCountable
というのは型を決めすぎていて少し扱いが難しい。
今回の例で言えばMax[TableGame]
として取り扱いたいところである。
これは新たな型パラメータと下限境界を導入することで改善可能である。
case class Max[+T <: Countable](initial: T) { val value = initial - def max(x: Countable): Max[Countable] = if (x.number > value.number) Max(x) else this + def max[U >: T <: Countable](x: U): Max[U] = if (x.number > value.number) Max(x) else this // >: が下限境界の指定 }
少しややこしいが、U >: T <: Countable
と書くことで、このU
の取るクラスの継承関係が下図の範囲であることを示している。
この状態で次のコードを実行すると、TableGame
として扱えていることがわかる。
val max = Max(Shogi).max(Mahjong) println(s"${max.value.description}は${max.value.number}です。プレイヤー数は${max.value.players}です。") // ==> "麻雀牌の数は136です。プレイヤー数は4です。"
反変(contravariant)
今回は取り上げなかったが、非変、共変の他に反変というものもある。
B extends A
であるとき、C[T]
についてC[A] extends C[B]
を満たすときのT
を反変という。
反変の指定は-
で行う。
trait Channel[-T] { def output(x: T) }
Scalaパターンマッチ集
定数パターン
expr match { case 1 => println("one") case _ => println("other") }
変数パターン
expr match { case a => println(s"expr is $a") case _ => println("none") }
型付きパターン
expr match { case a: MyClass => println("hit!") case _ => println("miss...") }
型パラメータは消されるので、下記のようなことはできない。
expr match { case a: MyValue[Int] => println(s"my value is ${a.read}") case b: MyValue[String] => println(s"my value is '${a.read}'") _ => println("none") }
コンストラクタパターン
expr match { case MyCaseAdd(l, r) => println(s"$l + $r = ${l + r}") case _ => println("none") }
MyCaseAdd
はケースクラスである必要がある。
シールドクラス
基底クラスにシールドクラスを利用することで、全ケース網羅のチェックができる。
sealed abstract class Expr case object Even extends Expr case object Odd extends Expr expr match { case Even => println("even") } // ==> warning: match is not exhaustive! // missing combination Odd
シーケンス、タプルパターン
expr match { case List(a, b, _*) => println(s"$a, $b, and more...") case (a, b) => println(s"$a, $b") case _ => println("none") }
_*
で可変長シーケンスとのマッチが可能。
パターン束縛
expr match { case p @ MyPoint(x, y) => println(s"${p.invert}") }
抽出子を使ったパターンマッチ
パターンガード
if
でパターンマッチにさらに条件をつけられる。
expr match { case n if n % 2 == 0 => println(s"even: $n") case n => print(s"odd: $n") }
ケースの変数展開
val (a, b) = expr
部分関数としてのケース
def twice(val: Option[Int]): Int = { case Some(n) => n * 2 case _ => 0 }
抽出子で柔軟なパターンマッチを行う
unapply(a: T): Option[U]
を実装したオブジェクトを抽出子と呼ぶ。
基本的にコンストラクタパターンにおけるパターンマッチはケースクラスに対して行うものであるが、抽出子を実装することで、ケースクラスでないオブジェクト(上記の例では数値)に対してもパターンマッチを適用することができる。
下記は1~100までの中で平方数を探すプログラムである。
import scala.math.sqrt object Square { def apply(num: Int) = num * num def unapply(num: Int): Option[Int] = { if (sqrt(num) % 1 == 0) Some(sqrt(num).toInt) else None } } for (i <- 1 to 100) i match { case Square(x) => println(s"$i is a square number of $x.") case _ => println(s"$i is not a square number.") }
返す値の数が可変長になる場合を考慮して、unapplySeq(a: T): Option[Seq[U]]
というものも用意されている。
下記はタプル2から公約数を求めるプログラムである。
import scala.math._ object CommonDivisor { def unapplySeq(num: (Int, Int)): Option[Seq[Int]] = { val gcd = grand(num._1, num._2) if (gcd == 1) { Some(Seq(1)) } else { val divisorsOfGcd = for { i <- 1 to ceil(sqrt(gcd)).toInt if gcd % i == 0 } yield { if (i > 1 && i < sqrt(gcd)) Seq(i, gcd / i) else Seq(i) } Some(divisorsOfGcd.flatten.sorted :+ gcd) } } private def grand(a: Int, b: Int): Int = { val (higher, lower) = (max(a, b), min(a, b)) if (lower == 0) higher else grand(lower, higher % lower) } } (120, 80) match { case n @ CommonDivisor(m @ _*) => println(s"All common divisors of $n are ${m.mkString(", ")}.") } // ⇒ "All common divisors of (120,80) are 1, 2, 4, 5, 8, 10, 20, 40."
Cloudwatch Logs Insightsの活用事例
11/27にリリースされた新機能Cloudwatch Logs Insightsが障害調査を進める上で有用だったため紹介したい。
続きを読む