10進数→2進数変換のアルゴリズムで出力を逆に読む理由

概要

手計算で10進数を2進数に手計算で変換するアルゴリズムとして,筆算を連鎖したあと,剰余を逆順に並べて出力とするものがありますが,なぜ逆順でうまくいくのか考えたことがなかったので,メモがてらまとめてみます.

概要としては,

  • ビットマスクとビットシフト
  • 数式

の両面から納得することができました.

アルゴリズム

10進数表記の数  aを2進数表記するとして,この記事で対象にするアルゴリズムは以下のとおりです.

  1.  aを2で割り,剰余と商a'を記録する
  2. a a' を代入し, aが2を下回るまで,これを繰り返す
  3. 剰余を逆順に左から並べる

 a=29 の場合を例に図解すると以下のようになります.

f:id:je6bmq:20190507204803p:plain
2進数変換の例(29の場合)

Python(3.6.2)のコード例は次のようになります.(実行例

bits_list = []
while a > 0:
    bits_list.append(a % 2)
    a = a // 2

bits_list.reverse()

この記事では,割り算の方向に対して出力の読み上げが逆方向に行われる理由について考えていきます.

準備

2進数で n桁の数は,式のように表されます.

 \sum_{i=0}^{n-1}  d_i  \times 2^i

 d_i は, i番目の桁の値を表し, d_i=0または d_i=1です. 例えば, a=29の場合は d_0=1 d_1=0 d_2=1 d_3=1 d_4=1となります.

もちろん,2進数,10進数は同じ数の違う表現というだけなので,

 2 \times 10^1 + 9 \times 10^0 = 1 \times  2^4 + 1 \times  2^3 + 1 \times  2^2 + 0 \times  2^1 + 1 \times  2^0

が成り立ちます.
(左辺:10進数表現,右辺:2進数表現)

ビットマスクとビットシフト

このアルゴリズムの要点は,2で割った場合の商と剰余を使い続けることにあります.ここで,2で割ることと2で割った場合の剰余をビット演算で表現することを考えます. このとき,2で割ることは 右に1bitシフトすること に,2で割った場合の剰余は 0b1との論理積 に対応します.

2で割った場合の剰余について

右シフトで商が得られることを用いると,ある数bについて2で割った剰余は a - (a>>1)<<1で求められます.
(正の数において,シフト時に足りないbitは0が補われるため)
ここで,最下桁以外の各桁の値はaと(a>>1)<<1の間で必ず等しくなるので,結果として0b1との論理積と同義です.

剰余を求めて記録し,2で割る操作は,まるで だるま落としのように,最下層の桁を取り出し,桁を落とす操作に対応します. したがって,最下桁が最初に取り出され,最上位桁が最後まで残るため,逆順に出力されます.

f:id:je6bmq:20190507215330p:plain
最下桁から順番に取り出される

ビット演算を用いて前述のコードを書き直すと,次のようになります.(実行例

bits_list = []
while a > 0:
    bits_list.append(a & 0x1)
    a = a >> 1

bits_list.reverse()

数式

2で割った場合の商と剰余を求めることは,数式から2をくくりだして,2 \times 商 + 剰余 の形式にすることと同義です. そこで,

 \sum_{i=0}^{n-1} d_i \times  2^i

を変形すると,

 =d_{n-1} \times 2^{n-1} + d_{n-2} \times 2^{n-2} + \cdots  + d_1 \times 2^1 + d_0 \times 2^0  =2 \times \left(d_{n-1} \times 2^{n-2} + d_{n-2} \times 2^{n-3} + \cdots + d_1 \times 2^0 \right) + d_0 \times 2^0

となります.ここで,剰余[tex: d_0 \times 20]を無視して(アルゴリズム上は記録することになりますが), この商の部分に着目すると,再び2でくくることができます. したがって,  =2 \times \left( 2 \times \left (d_{n-1} \times 2^{n-3} + d_{n-2} \times 2^{n-4} + \cdots + d_2 \times 2^0 \right)+ d_1 \times 2^0 \right) + d_0 \times 2^0

となります.

このように,剰余の項を無視して2でくくりだす,を繰り返すことができ,これが 最下桁の値から 剰余として分離することに相当するため,出力を逆順に読むことになります.

まとめ

10進数→2進数変換アルゴリズムの出力を逆順に読み上げる理由について,

意味付けを行いました.

補足

個人的には再帰で書くほうが好みです.(実行例

# start
def convert_to_bin(n, acc_list=[]):
    return reversed(acc_list) if n == 0 else convert_to_bin(n // 2, acc_list + [n % 2]) 
# end

a = 29 
result = "".join([str(b) for b in convert_to_bin(a)])

print(result)

ユークリッドの互除法を多項式に拡張して,(Rustで)実装する

はじめに

この記事はTUT Advent Calendar 2018 三日目の記事(になる予定だった記事)です. 遅れてしまい申し訳ありません.

二日目はT_T3_M5さんの「けんきうのおはなし」でした. 生き物はどうやってものを「見て」いるか?という観点でしたが, 「人は見たいものを見たいように見る」みたいな言葉を思い出しました.

自己紹介

今年は二つのTUT合同でのAdCということで,開催おめでとうございます.
(そういえば,確かフィンランドの方にもTUTってありましたよね.)

はじめまして(?),「むしぱん」です.東京じゃない方の人間です. プログラミング,数学が好きです.
よく使うプログラミング言語はRust,Juliaです.最近は全然書いてないですが,Kotlin,Elixirも好きです.

私も「けんきうのはなし」をするか?と思いましたが用意に身元を特定できそうなので避けようと思います.

私はプログラミングと数学が好きなので,この記事では数学とプログラミング,両方に関わる話題として, (自然数の)最大公約数を求めるアルゴリズムユークリッドの互除法」を多項式に拡張し,(一般化して)Rustで実装した話をします.

ユークリッドの互除法自然数

そもそも,一般に言われるユークリッドの互除法自然数の最大公約数(Greatest Common Divisor)を求めるアルゴリズムです.

自然数{a,b}に対して,以下の式が成り立つとします.つまり, {a}{b}で割ったときの商が{q}で,あまりが{r} ということです.

{a= bq + r}

ここで,最大公約数を求める関数を{\mathrm{gcd}(x,y)}とおくと,以下の式が成り立ちます.

{\mathrm{gcd}(a,b)=\mathrm{gcd}(b,r)}

ここで重要なのは,常に{r\leq b}が成立する点です. 上の式を繰り返し適用することで最大公約数を求めます.このとき,{r\leq b}であることから,徐々に{a,b}の値が小さくなり,最終的にあまり{r=0}となります. あまりが0となったときの{b}の値が最大公約数 となります.

例えば,{a=28,b=6}の例を考えます.最終的に4割る2を計算することとなり,あまりが0であることから,最大公約数は2となります.

f:id:je6bmq:20181204000324p:plain
ユークリッドの互除法による最大公約数の導出({a=28,b=6}

これをプログラムとして記述すると,次の様に記述できます.
Rustにおける実装例ですが,他の言語でも同様の実装になるかと思います.
{a \leq b}だとしても,一度{q=0}となって大小関係が逆転するのみなので,大小関係のチェックは不要です)

fn gcd(a: u32, b: u32) -> u32 {
     if b == 0 { a } else { gcd(b, a % b) }
}

多項式における最大公約数って?

この記事の目的は,この「ユークリッドの互除法」を多項式に応用することでした.今回は各係数が実数の,{x} についての多項式を考えます.
例えば,{ x^2+1}や,{x^3 +\sqrt{2}x + 1}が挙げられます.

この「({x}についての多項式)における最大公約数」を考える前に,一般的な(自然数における)最大公約数とはどういう数か考えます.

28と6の最大公約数,280と588の最大公約数({\mathrm{gcd}(288,588)=28})を例に, 両整数の素因数分解と最大公約数に着目すると, 実は, 最大公約数は共通の素因数の積 になります.

f:id:je6bmq:20181204004508p:plain
最大公約数は,共通の素因数の積になる

多項式における最大公約数も同様に, 多項式素因数分解(に対応する操作をした場合の)共通の素因数(に対応するもの)の積 である,とします.

では,この多項式における素因数分解(に対応する操作)と素因数(に対応するもの)は何なのでしょう.
そもそも,自然数における素因数分解は,最大限細かく素数の積で表す操作(分解)でした.そして,素数は,1と自身以外を約数に持たない数,すなわち,1と自身の積以外に分解できない数でした.

多項式における「分解」として,一番最初に学校で習う操作があります.「因数分解」です.因数分解は,ある多項式を最大限細かく別の多項式の積で表す操作でした.
実はこの因数分解したときの各多項式(例:{x^2-1=(x+1)(x-1)}における{(x+1)}{(x-1)})には「既約多項式」という名前がついています.

そして,自然数における素数素因数分解が,多項式における既約多項式因数分解に対応しています. (厳密には対象にしている集合が整数か,自然数か,実数か,によってある多項式が既約多項式であるかどうかは変わってきますが…)

したがって,多項式における最大公約数は,多項式で共通の既約多項式の積となります.

f:id:je6bmq:20181204012259p:plain
自然数多項式の「分解」の対応

ユークリッドの互除法多項式

では,自然数におけるユークリッドの互除法多項式に拡張します.つまり,これによって多項式の最大公約数(共通の既約多項式の積)が求められればよいということです.

多項式{f(x),g(x)}に対して,以下の式が成り立つとします.つまり, {f(x)}{g(x)}で割ったときの商が{q(x)}で,あまりが{r(x)} ということです.

{f(x)= g(x)q(x) + r(x)}

自然数同様,最大公約数を求める関数を{\mathrm{gcd}(f(x),g(x))}とおくと,以下の式が成り立ちます.

{\mathrm{gcd}(f(x),g(x))=\mathrm{gcd}(g(x),r(x))}

例えば,次の二つの多項式について,互除法を適用します.各々の因数分解から,最大公約数は{(x-1)(x^2+1)=x^3-x^2+x-1}であることがわかります.

 {f(x) =2 x^5 - 7 x^4 + 4 x^3 - 4 x^2 + 2 x + 3= (x-1)(x-3)(2x+1)(x^2+1)}

 {g(x)  = x^4 -1 = (x-1)(x+1)(x^2+1)}

では,互除法を使って最大公約数が求めます.

f:id:je6bmq:20181204145738p:plain
多項式の最大公約数を互助法によって求める例

互除法によると,最大公約数は{4x^3-4x^2+4x-4}だそうです.
あれ?{(x-1)(x^2+1)=x^3-x^2+x-1}ではない?と思うかもしれませんが実は{4x^3-4x^2+4x-4=4(x-1)(x^2+1)}であるため,両多項式とも{4x^3-4x^2+4x-4}によって割り切れるのです.

このように,多項式では最大公約数の定数倍の多項式ではすべて割り切れてしまうため,実際は既約多項式の最高次の係数を1に正規化して扱うことがあります.
例えば,先ほどの互除法の例の最後の商,{\frac{1}{4}x+\frac{1}{4}}から{\frac{1}{4}}をくくり出して,{\frac{1}{4}(x+1)}と扱えば,定数倍の差を吸収できます.

最大公約数関数を多項式に拡張,実装する(Rust)

ユークリッドの互除法を行う上で自然数多項式共通の操作は四則演算と剰余演算,余りがゼロであるという判定です. つまり,四則演算と剰余演算ができて,かつゼロが定義されていれば互除法が適用できることが示唆されます.

そこで今回は 新たに多項式を表すPolynomial型を定義し,Polynomial型を実装し,ゼロの概念と等号判定を導入する ことで自然数多項式両方に適用できる最大公約数関数を実装します. 詳細の実装の説明は省略して,概要のみ説明します.詳細はこちらのGistを参照ください.(思いの外,数式パートが長引いたので…)

Rustでは,トレイト(他の言語でいうインターフェースのようなもの)を用いて関数の型制約を表現できるので,次の様に実装できます.

fn gcd<T>(a: T, b: T) -> T
where
    T: Add<Output = T>
        + Sub<Output = T>
        + Mul<Output = T>
        + Rem<Output = T>
        + Zero
        + PartialEq
        + Clone,
{
    if b == <T as Zero>::get_zero() {
        a
    } else {
        gcd(b.clone(), a % b)
    }
}

ここで,型TはAddトレイト,Subトレイト,Mulトレイト,Remトレイト,Zeroトレイト,PartialEqトレイトを実装している必要があることを意味します.そして,各トレイトは次の意味を持ちます.

  • Addは加算可能,かつ計算結果の型がT
  • Subは減算可能,かつ計算結果の型がT
  • Mulは乗算可能,かつ計算結果の型がT
  • Remは剰余演算可能,かつ計算結果の型がT
  • PartialEqは == によって同値関係が確かめられること

上記のトレイトは標準ライブラリに搭載されているので,自然数(符号なし整数型)には当然実装されています.したがって,新たにPolynomial型にも実装することとしました.

新たに定義したのはZeroトレイトです.Zeroトレイトは,名前の通りその型における ゼロ を定義したいため,次の様な実装にしました.
符号なし整数(ここでは32bitのみ対象)では0をそのまま返し,Polynomial型においては0次の項の値が0の多項式を返します.

impl Zero for u32 {
    fn get_zero() -> u32 {
        0
    }
}
impl Zero for Polynomial {
    fn get_zero() -> Polynomial {
        Polynomial::new(&vec![0f32], false)
    }
}

実行結果

一般化したgcd関数はもちろん32bit符号なし整数型にもそのまま適用可能ですが,ここでは先ほどの二つの多項式

 {f(x) =2 x^5 - 7 x^4 + 4 x^3 - 4 x^2 + 2 x + 3= (x-1)(x-3)(2x+1)(x^2+1)}

 {g(x)  = x^4 -1 = (x-1)(x+1)(x^2+1)}

の最大公約数を計算した結果を示します.ここで,^ (ハット)の後ろの数字が指数を表します.

実際に,先ほどと同様に最大公約数を求めることができています.

f:id:je6bmq:20181204153247p:plain
一般化したgcd関数(Rust)の実行結果

まとめ

この記事では,最大公約数を求めるアルゴリズムユークリッドの互除法自然数から多項式に拡張して,かつプログラミング言語Rustで実装できることを確認しました. 少なくともRustではトレイトで型の制約を表現できるので, 数学における概念と満たすべき性質 がうまく対応付けられて嬉しいと思っています.

多項式が既約多項式かどうか,といったような概念は有名な理論である「 ガロア理論 」とも関わりが強く, 私がこの実装を試みたきっかけにもなっているので,興味のある方は調べてみてはいかがでしょう.(というか私も教えてほしい.)

本来の四日目の担当はyanteneさんで,「何書くかまだ何も考えてない!遅れたらごめんなさい!」とのことです. この記事自体が遅れてしまったので何も言えませんが期待しています.

Juliaの構造体とジェネリクスについていろいろ勘違いしていた話

Juliaについて

最近、数値計算用のプログラミング言語、Juliaにはまっています。
Juliaは、動的型付けでありながら、ユーザ(プログラマ側)が型を明示したり、関数単位のJIT(Just-In-Time)コンパイルに伴う最適化によって高速に実行可能であるため、注目されています。

Juliaにおいても、多言語のジェネリクスのように静的型付け言語にあるようなサブタイプ、スーパータイプを指定することによる型の制約を設けることが可能です。 (2018年5月29日時点、Juliaのバージョンは0.6) 今回はJuliaの構造体とジェネリクス(?)に少々はまったので記しています。

コンストラクタにおける勘違い

まず、以下のようなコードを考えます。
ここで、TはAbstractFloatのサブタイプであることを意味しており、 Hoge{T}(_a::T) = new(_a) はコンストラクタの定義を表しています。 また、AbstractFloatは64bit浮動小数型であるFloat64、32bit浮動小数型であるFloat32のスーパータイプとなっています。

struct Hoge{T<: AbstractFloat}
      a::T
      Hoge(_a::T) = new(_a)
end

このコードを実行すると、型パラメータと制約を明記するよう、警告されます。 Juliaでは型パラメータTをそのまま利用できるわけではないようです。これは 構造体の定義外にもコンストラクタを定義できることから、構文を統一されるためのもの だと推測しています。
(Juliaのドキュメントでは、コンストラクタの定義位置が構造体定義の内外に応じて、Innner、Outerと区別されている)

WARNING: deprecated syntax "inner constructor Hoge(...) around C:\ProjectYukito\julia\famiconlike_sound_spectrum.jl:21".
Use "Hoge{T}(...) where T" instead.

そこで、次のように変更します。これにより、警告が無くなりました。

struct Hoge{T<:AbstractFloat}
    a::T
    Hoge{U}(_a::U) where U<:AbstractFloat = new(_a)
end

ジェネリクスにおける勘違い

次に、Hoge型の変数を宣言することを考えます。

hoge = Hoge(0.5)

一見問題ないように見えますが、 このコードは、以下のエラーのため実行できません。

ERROR: LoadError: MethodError: Cannot `convert` an object of type Float64 to an object of type Hoge
This may have arisen from a call to the constructor Hoge(...),
since type constructors fall back to convert methods.

=コンストラクタがconvert関数として扱われるが、convert関数がFloat64→Hogeの変換に対応していない(?)

コンストラクタが引数の型に対応していない場合、convert関数でエラーを出力することは型変換に関するJuliaのドキュメントにも記されています。

Conversion and Promotion · The Julia Language

問題は、 Float64に対してHogeのコンストラクタが対応していない点 です。
Float64はAbstractFloatのサブタイプであるはずなので、エラーが出力されることが解せませんでした。そこで、Float64であることを明示してみます。

hoge = Hoge{Float64}(0.5)

これにより、初めて正常に実行されました。 ちなみに、Float64の代わりにAbstractFloatのサブタイプであるFloat32を指定すると、同様に cannnot convert ... のエラーが出力されます。一方で、AbstractFloatのサブタイプでない型(UInt32など)を指定すると、型制約に伴うエラーが出力されます。

これらのことから、 Juliaでは、引数の型に応じて推論の結果型パラメータの値が定まるのではなく、型を明示する必要がある という結論に至っています。

まとめ

普段Rustばかり書いているせいか、Juliaのジェネリクスについて混乱してしまいました。

  • 内部コンストラクタ(Innner-Constructor)において、構造体の定義に用いる型パラメータは直接適用されない
  • 変数の宣言時、型パラメータは明示する必要がある(引数の型推論から確定するわけではない?)

WindowsでSource Code Pro をビルドしようと思うも一筋縄ではいかなかった話

最近、エディタをSpacemacsに乗り換えました。 Spacemacsのデフォルト設定では、フォントがSource Code Proなので、Source Code Proが入っていない場合は起動後にWarningが出ます。
おそらく代替フォントで描画されているかと思いますが、特に日本語の描画が大変厳しいようで、お世辞にも綺麗とは言えない状況でした。

そこで、Source Code Proを導入することとしました。

Source Code Pro(GitHub)にもあるように、 リリースページからダウンロードすればよい のですが、 github.com 見事にそのリンクの情報を見逃した私は、ソースコードからビルドすることにしました。

基本的にはREADMEにしたがって行えばよいのですが、私の環境(Windows10)ではbuild.cmdの実行時にエラーが発生しました。

syntax error at "}" missing ";" 

当該リポジトリのIssueにも同様の事例が報告されていませんでしたので、困惑していたのですが、別リポジトリのIssueを参考に解決することができました。 つまり、familyTables.feaの2行目のセミコロンを削除する ことで対処できました。

github.com

いやわざわざビルドしなくていいのに、と言われればそれまでなのですが。。。

Rustで数値シミュレーションをする際に使用しているライブラリ(クレート)の紹介

この記事はTUT Advent Calendar 2017の18日目の記事です。

概要

普段私はRustを使ってシミュレータを作成しているのですが、そこでお世話になっているライブラリ(クレート)を簡単に紹介します。
今回紹介するライブラリ(用途)は以下の2つです。

  • rayon (データ並列化)
  • statrs (様々な確率分布に従う乱数生成)

rayon

すでにいくつかの記事で紹介されていますが、rayonによって主に以下の処理を簡潔に行うことができます。両者とも、Work stealingと呼ばれる方法で実現されているそうです。

  • スレッドによる並列処理
  • 再帰関数の分割処理

スレッドによる並列処理

rayonでは、Parallel Iteratorの導入によって通常のイテレータに対する関数(mapなど)の処理を並列化することができます。D言語でいうstd.parallelismに似ているかもしれません。 ただし、Rustのシステムによって並列時の安全性は保証されています。つまり、標準ライブラリのスレッド同様、Sendトレイト、Syncトレイトの制約によってコンパイル時に検査が行われます。

iter関数やinto_iter関数が実装されている型は基本的にParallel Iteratorに変換することができ、以下のような対応関係を持ちます。

コード例は以下のとおりです。

extern crate rayon;
use rayon::prelude::*;

fn main() {
    (0..100)
    .into_par_iter()
    .map(|i| i * 2)
    .for_each(|x| println!("{}",x));
// 200未満の偶数が出力される
}

大変便利ですが、Parallel Iteratorに対してcollect::<Vec<_>>()のようなことはできず、 あらかじめ格納先のベクタをミュータブルとして宣言したのち、collect_into関数を用いて格納する必要があります。

コード例は以下のとおりです。

extern crate rayon;
use rayon::prelude::*;

fn main() {
    let mut even_vec = vec![0;100];
    (0..100)
    .into_par_iter()
    .map(|i| i * 2)
    .collect_into(&mut even_vec);
// 200未満の偶数がeven_vecに格納される
}

並列処理時のスレッド数は自動的に決定されますが、スレッド数を能動的に設定したい場合は、num_threads関数によって設定可能です。
また、実行時にスレッド数を設定したい場合は、環境変数RAYON_NUM_THREADSの値を設定してから実行することで対応できます。

再帰関数の分割処理

join関数によって、再帰関数を効率的に並列処理します。
rayonのGithubリポジトリの例が簡潔であるため、引用します。 コード例は、並列化したクイックソートのようです。

fn quick_sort<T:PartialOrd+Send>(v: &mut [T]) {
    if v.len() <= 1 {
        return;
    }

    let mid = partition(v);
    let (lo, hi) = v.split_at_mut(mid);
    rayon::join(|| quick_sort(lo), || quick_sort(hi));
}

リポジトリのREADMEによれば、

Note though that calling join is very different from just spawning two threads in terms of performance. 
This is because join does not guarantee that the two closures will run in parallel. 
If all of your CPUs are already busy with other work, Rayon will instead opt to run them sequentially. 
The call to join is designed to have very low overhead in that case, so that you can safely call it even with very small workloads (as in the example above).

とのことですので、join関数の引数のクロージャを、可能なら並列処理し、CPUの負荷が高い場合は逐次的に処理するよう、うまくスケジューリングされる(?)ようです。

statrs

statrsはRustにおける統計的な数値計算のためのクレートで、特に私はdistributionモジュールを頻繁に利用しています。distributionモジュールを利用することで、各種確率分布に従う乱数を容易に生成することができます。
ただし、rust statrsで検索すると検索エンジンが変に気を利かせて別の検索ワードを推奨してくるので注意です。

離散型分布、連続型分布問わず様々な分布に対応しており、以下の分布ごとに構造体が定義されています(非常に多い)。また、分布の期待値、分散を返す関数が各分布に実装されており、離散型確率分布に対しては確率質量関数の値、連続型確率分布に対しては確率密度関数の値を返す関数が実装されています。

さらに、sample関数(rand::Rngトレイトを実装する乱数生成器を引数)の実装によって、各分布に従う乱数を生成可能です。 rand::Rngトレイトを実装している乱数生成器であればよいため、StdRngやThreadRng、XorShiftなど様々なものに対応しています。

  • 離散型確率分布
    • ベルヌーイ分布
    • 二項分布
    • カテゴリカル分布(Categorical Distribution)
    • 離散一様分布
    • 幾何分布
    • 超幾何分布
    • 多項分布
    • ポワソン分布
  • 連続型確率分布
    • ベータ分布
    • コーシー分布
    • カイ分布
    • カイ2乗分布
    • ディリクレ分布
    • アーラン分布
    • 指数分布
    • F(Fisher-Snedecor)分布
    • ガンマ分布
    • 逆ガンマ分布
    • 対数正規分布
    • 正規分布
    • パレート分布
    • t分布
    • 三角分布
    • 連続一様分布
    • ワイブル分布

本記事ではdistributionモジュールにのみ着目しましたが、他のモジュールにてベータ関数やエラー関数などの複雑な関数の値を計算する関数も用意されているようです。

まとめ

rayonやstatrsは、数値シミュレーションをするうえで、大変有用です。並列処理により計算時間を削減することができ、randクレートでは対応していない様々な確率分布に従う乱数生成を行うことで様々なシミュレーションが可能です。
また、Rustはコンパイラが大変優秀なので、コンパイル時の検査のおかげでシミュレーション実行時のエラーがなく、重宝しています。その一方で、コンパイルを通すのは大変ですが。

私の身の回りでもRustが流行ればいいな、と思うこの頃です。

apt-get dist-upgradeで無線LANに接続できなくなった

概要

Raspbian jessie on Raspberry Pi (1st)の環境で、apt-get dist-upgradeを実行したら無線LANに接続できなくなったので、その解決(?)までの過程を記事に残します。

問題

dist-upgrade実行後に、

  • Raspberry Piが設定した固定IPではなくなる
  • そもそもwlan0にIPが割り振られなくなる

といった問題が発生しました。

解決まで

IPが降られなくなる

$ dmesg

でログを確認してみると、wpa_supplicantがCTRL-EVENT-DISCONNECTEDというエラーで異常終了している様子。

$ cat /etc/wpa_supplicant/wpa_supplicant.conf  

で設定を見てみると、先頭に下記の謎の設定が自動的に追加されていました。

ctrl_interface=DIR=/var/run/wpa_supplicant GROUP=netdev
update_config=1

この設定を削除して元のwpa_supplicant.confに戻すと、無線LANに接続できました。

固定IPでなくなる

dhcpcd.confを覗いてみると、設定が初期値に戻されていたので、以前と同様に設定を行うことで固定IPの設定も難なくできました。
dist-upgradeの途中で設定ファイル群を更新したことが原因のようです。

まとめ

dist-upgradeの途中で設定を更新するときは要注意。

TDD challenge #1 に参加してきました

概要

2017年8月18日に株式会社ミクシィ 渋谷本社にて開催されたTDD challengeに参加してきました。

このワークショップは、テスト駆動開発(TDD: Test Driven Development)の講義と、ペアプログラミングの形式をとった実習から構成されていて、講義と演習を交互に行うものでした。

当日の流れ

開始前

11:00より受付とのことだったので、あらかじめ場所を確認しておいて、定刻どおりに受付まで行いました。
JR渋谷駅のすぐ近くだったので、迷わずに済みました。

各種設定(演習用)

予め決められたペアで演習を行いました。パートナーの方と共通して使用できる言語がJavaしかなかったので、使用言語はJavaとしました。

全体としては、Javaを使用するペアが多数でしたが、他にもRubyPythonなどを使用するペアもあって、多種多様でした。
とはいえ、私たちのペアは両者ともにしばらくJavaを書いていなかったので、些か不安でした。

講義

なぜTDDが必要なのか、どのような状況でTDDがその威力を発揮するのか、TDDを始めるにあたって意識すべきことは何か、といった点に重点を置いた講義だったように感じました。

TDDでは、その名にあるとおり、

  1. テストを書く
  2. テストを失敗させる
  3. (テストを通すように)実装する

の順で開発を行います。リファクタリングは最後、つまりテストが通っている前提で行うべき、とのことでした。 特に、TDDにおいて意識すべきことやメリットが次節の演習で大きく効いていた印象を受けました。

演習

講義の合間合間に複数の課題が発表されて、要求された仕様を満たすようにペアでTDDによる実装を行います。

一見実装が楽に思える仕様でも、TDDを意識して、テストを先に完成させることに重点を置きました。 私たちのペアはJavaを使用したので、テストフレームワークとしてJUnitを採用しました。
両者ともにJUnitは未経験だったため、講師の方々に助けて頂きながら試行錯誤しました。

演習の前半ではつい実装の方に手を伸ばしてしまいそうになっていましたが、後半ではテストを書くことに多くの時間を割くようになっていました。
各課題ともに1~2時間程度(正確に測ったわけではないのでそれ以上の可能性もある)の時間が設けられていましたが、テストの作製に時間を要したこともあって、すべての課題を完成させることはできませんでした。

しかし、テストを作成するにあたって、テストがコード自体(クラスなど)の設計に直結しているように感じて、テストを完成させたあとの実装は想像以上に速く行うことができたように思いました。

その他

エンジニアの方々とともに昼食をいただきました。8月18日が「お米の日」(「米」を分解すると八 十八 になることから。米寿の由来と同様?)ということで、釜めしをいただきました。お茶漬け用の出汁もあり、二重の楽しみがあって大変おいしくいただきました。

食事中にはエンジニアの方と研究の話題や普段のコーディング、プログラミング言語全般など、様々なお話をさせていただいて、大変有意義な時間となりました。

まとめ

普段私はプロジェクト(もしくはオブジェクト指向としてのクラス)を設計するとき、必要なメソッド群を脳内で雑に実装を始める、といったことをしていたのですが、実際はそれはTDDに直結していて、そのまま置き換えられるように感じました。
TDDではテストがそのまま実装される関数群を網羅していて、かつテストケースとして期待する振る舞いを記述することから、むしろ仕様がテストに表れている印象を受けました。
期待される動作がテストに書かれているため、実装の際に混乱することを抑制することができ、実装をスムーズに行うことができたように思います。

言語によってテストフレームワークは異なりますが、幸い普段私が使用する言語は優秀なフレームワークが整っているようなので、今後TDDを活用していきたい、と強く感じたワークショップとなりました。

最後になりますが、ワークショップを通して丁寧に指導してくださった講師の皆様、本当にありがとうございました。 また、参加者の皆さん、お疲れ様でした。