【技術記事】CSSの設計方法をまとめてみた~BEM編~

CSSの設計方法をまとめてみた~BEM編~

BEMについて、簡単にまとめる。
詳細な部分に関しては以下の記事がわかりやすかったので、参考にされると良いと思う。

bem-methodology-ja/index.md at master · juno/bem-methodology-ja

BEMという命名規則とSass 3.3の新しい記法 - アインシュタインの電話番号

BEMとは

Block、Element、Modifierの略。
DOMを構成する各要素をBlock、Element、Modifierのどれかに当てはめて命名する。
正直なところCSSのclass名は冗長になるので最初は冗長に感じるかもしれないが、がっつり命名規則が決まるので段々と楽に感じてくるかもしれない。

BEMのメリット

BEMの公式ドキュメントでは、以下の問題を解決するとしている。
なお、以下の英語の部分はBEMの公式ドキュメントから引用しているものである。

BEM PROVIDES THE SAME RULES TO ACHIEVE CODE CONSISTENCY

【意訳】
BEMはコードの一貫性を獲得するための同一ルールを提供する。

Common approach for all technologies: HTML, CSS, JavaScript, docs, tests, etc.

【意訳】
HTML, CSS, JavaScript, docs, testsなどのあらゆる技術に対しての共通のアプローチである。

GROW AND SCALE YOUR CODEBASE

【意訳】
(コードベースが)育ち、あなたのコードベースをスケールさせる。

Most projects use the same components. Code reuse significantly reduces price and time of development.

【意訳】
大半のプロジェクトは、同じコンポーネーネント(部品や構成要素)を使用する。
コードの再利用によって、開発のコストと開発にかかる時間が著しく減少する。

INCREASE PRODUCTIVITY

【意訳】
生産性を上げる。

Simplicity of updates and scalability increases productivity.

【意訳】
アップデートと拡大の容易さは、生産性を上げる。

TEAM WORK

【意訳】
チームワーク

Common terminology provides ability for developers to rapidly switch projects — everything is familiar.

【意訳】
共通した専門用語は、開発者がプロジェクトに(新規に)加わるのにやくに立つ。

DO LESS, GET MORE

【意訳】
することは少ないが、得るものは多い。

Common rules help to automate process. Code may be partially autogenerated.

【意訳】
共通のルールによって、プロセスを自動化が促進される。
コードが部分的に自動生成されるようになるかもしれない。

SUITABLE FOR ANY PROGRAMMING LANGUAGE OR ANY FRAMEWORK

【意訳】
あらゆるプログラミン言語及びフレームワークに適している。

Methodology provides language agnostic practices to increase code reliability and reuse.

【意訳】
方法論は、言語にとらわれないコードの確実性と再利用を促進する習慣を提供する。

EASY TO LEARN

【意訳】
学習しやすい。

You can read all the methodology during your morning coffee.

【意訳】
朝コーヒーを飲んでいる間に全ての方法論を読める(くらいに学習が容易だ)。

PROMOTE REUSE

【意訳】
再利用を促進する。

Code grows following predefined rules.

【意訳】
あらかじめ定義されたルールに基づいて、コードが成長していく。

BEMのデメリット

class名が冗長になる。

Block、Element、Modifierの各説明

Block

アプリケーションやWEBサイトを構成する塊。
ページを構成する大きめの部品だと考えれば良いと思う。
ブロックは、自身の中に別のブロックを含む場合がある。
パソコンで例えると、画面やキーボード(一個一個ではなくて各ボタンの集合として)、トラックパッドなど。

Element

Blockの中に存在し、Blockを構成するための各要素だと考えれば良い。
パソコンで例えると、画面のベセルだったり、キーボードの中の各ボタン(AとかEnterなど)など。

Modifier

BlockやElementの中で、状態やレイアウトが変化するプロパティ(性質)。
名前と値のセットである。

記述方法

BlockElement--Modifier(name)_Modifier(value)と言う形で記述する。(Block(アンダースコア2つ)--Element(ハイフン2つ)Modifierの名前部分_(アンダースコア1つ)Modifierの名前部分)。
Block、Element、Modifier共に、名称が複数の単語から構成される場合は、 hoge-foo のような具合でハイフン1つでつなぐ。

SCSSと一緒に使う

SCSSの & とBEMはすごく相性が良いようだ。
& は、ブロックの親セレクタを参照する。

参考 BEMをSassで快適に書く方法 | maesblog

CSSの常識が変わる!「Compass」の基礎から応用まで! | 株式会社LIG

記述例

実際に、BEMを導入し、SCSSの方式で簡単なサンプルを記述してみる。

HTMLは以下。

<section class="block-A">
    <div class="block-B">
        <div class="block-B__element-B1 block-B__element-B1--modifier-B1">B1</div>
        <div class="block-B__element-B2 block-B__element-B2--modifier-B2">B2</div>
    </div>
    <div class="block-C">
        <div class="block-C__element-C1 block-C__element-C1--modifier-C1">C1</div>
        <div class="block-C__element-C2 block-C__element-C2--modifier-C2">C2</div>
    </div>
</section>

SCSSは以下。

.block-A {
  width: 100%;
  padding: 20%;
  .block-B {
    &__element-B1 {
      width: 50%;
      background-color: black;
      &--modifier-B1 {
        color: white;
      }
    }

    &__element-B2 {
      width: 50%;
      background-color: blue;
      &--modifier-B2 {
        color: yellow;
      }
    }
  }
  .block-C {
    &__element-C1 {
      width: 50%;
      background-color: aquamarine;
      &--modifier-C1 {
        color: orange;
      }
    }
    &__element-C2 {
      width: 50%;
      background-color: deeppink;
      &--modifier-C2 {
        color: green;
      }
    }
  }
}

SCSSを以下のコマンドでコンパイルして生成されたCSSファイルが以下。

コマンド

sass --style expanded bem.scss:bem.css

生成されたcss

.block-A {
  width: 100%;
  padding: 20%;
}
.block-A .block-B__element-B1 {
  width: 50%;
  background-color: black;
}
.block-A .block-B__element-B1--modifier-B1 {
  color: white;
}
.block-A .block-B__element-B2 {
  width: 50%;
  background-color: blue;
}
.block-A .block-B__element-B2--modifier-B2 {
  color: yellow;
}
.block-A .block-C__element-C1 {
  width: 50%;
  background-color: aquamarine;
}
.block-A .block-C__element-C1--modifier-C1 {
  color: orange;
}
.block-A .block-C__element-C2 {
  width: 50%;
  background-color: deeppink;
}
.block-A .block-C__element-C2--modifier-C2 {
  color: green;
}

ブラウザに表示される画面
bem.png

参考にさせていただいたサイト

bem-methodology-ja/index.md at master · juno/bem-methodology-ja

BEMという命名規則とSass 3.3の新しい記法 - アインシュタインの電話番号

BEMを参考にしたCSS設計 - Qiita

bem-methodology-ja/definitions.md at master · juno/bem-methodology-ja

BEM公式

BEMをSassで快適に書く方法 | maesblog

CSSの常識が変わる!「Compass」の基礎から応用まで! | 株式会社LIG

Qiitaでも同一の投稿を行っている。

【技術記事】RxJSの基本をまとめてみた~基本的な概念編(Observable、Observer、Subscriptionなど)~

RxJSの基本をまとめてみた~基本的な概念編~

Angularで使用していたRxJSだが、もっと深くちゃんと理解しようと思い学習したので、これから何回かに分けてまとめる。
仕組みを理解するために、ちょっと冗長な書き方をするところがある。
「これどうなの?」とか「意訳おかしくね?」という部分があったら、ご指摘いただけるとありがたいです。

今回の範囲

今回は、RxJSやリアクティブプログラミングの基本的な概念である、データストリーム、Observable、Observer、Subscription関連についてまとめたいと思う。
今回は、RxJSの公式ドキュメントをかなり参考にさせていただいた。

RxJS

RxJSとは

JavaScript向けのReactive Extensions ライブラリで、リアクティブプログラミングを行うためのもの。
Reactive-Extensions/RxJS: The Reactive Extensions for JavaScript RxJS API Document

リアクティブプログラミング

リアクティブプログラミングとは

リアクティブプログラミングについて様々な説明のされ方があるが、以下の説明が一番ピンときた。

リアクティブプログラミングとは、通知されてくるデータを受け取るたびに関連したプログラムが反応し(リアクション)して、処理を行うようにするプログラミングの考え方です。

引用元 : 須田智之 (2017/2/16)『RxJavaリアクティブプログラミング』 翔泳社

データストリーム

リアクティブプログラミングでは、あらかじめ用意されている固定長のデータだけではなく、随時発生するデータを処理することができる。
この随時データが生成されて、その都度流れてくるデータの流れをデータストリームという。

参考 : 「RxJS」初心者入門 - JavaScriptの非同期処理の常識を変えるライブラリ 須田智之 (2017/2/16)『RxJavaリアクティブプログラミング』 翔泳社

RxJSの基礎

ProducerとConsumer

Producer

データを生成し、そのデータを通知する責務を持つ。
RxJSではObservableがこれに当たる。

An Observable emits items or sends notifications to its observers by calling the observers’ methods.

引用元 : ReactiveX - Observable

【意訳】Observableは、アイテム(データなど)を排出し、observerのメソッドを呼び出すことによって、自身のobserverに通知を送信する。

Consumer

データを受信し、必要な処理を行う責任を持つ。
RxJSではObserverがこれに当たる。

In ReactiveX an observer subscribes to an Observable. Then that observer reacts to whatever item or sequence of items the Observable emits.

引用元 : ReactiveX - Observable

【意訳】ReactiveXでは、ObserverはObservableを購読する。さらに、ObserverはObservableが生成するいかなるアイテム(データなど)やアイテム(データなど)のシーケンスに対して反応する。

ObservableとObserverとオペレータとsubscribe

Observableがデータを生成し、データを通知する。

Observableがデータを生成し、Observerにデータを通知し、届けるまでに、様々な前処理を行うことができる。
この前処理をオペレータという。
オペレータを使って前処理をすると新しいObservableを生成することになる。
オペレータに関しては別で記事を書きたいと思っている。

そして、Observableシーケンス(データストリーム)から流れてくるデータをObserverがsubscribe(購読)する。その際に、受け取ったデータに関して、必要な処理を行う。

上記の処理の流れをエラーが出るか、完了するまで続ける。
エラーや完了については後述する。

ただ、実際のコードの中だと「Observerってどこで生成されているの!?」と思う ことが多々ある。
それは以下のような理由らしい。

Internally in observable.subscribe, it will create an Observer object using the first callback argument as the next handler. All three types of callbacks may be provided as arguments

引用元 : [RxJS Overview](http://reactivex.io/rxjs/manual/overview.html#subscribing-to-observables

【意訳】
observable.subscribe で内部的に、最初のコールバックの引数をnextハンドラーとして使用しているObserverのオブジェクトを生成する。3つのコールバックの全部のタイプが、引数として提供され得る。

Observerの3つのコールバック

Observers are just objects with three callbacks, one for each type of notification that an Observable may deliver.

引用元 : http://reactivex.io/rxjs/manual/overview.html#subscribing-to-observables

【意訳】
Observerは、Observableが届けてくるであろう各々のタイプの通知に対する3つのコールバックを持ったただのオブジェクトである。

Observerは、Observableをsubscribe(購読)し、受け取ったデータを処理する。
ここでいう3つのコールバックがその処理に当たる。
3つのコールバックとは、 nexterrorcomplete である。

next

Observableがデータを生成する度にObservableによって呼び出される。
Observableによって生成されたデータを引数に取る。

error

エラーが発生したことを通知する。
これが呼び出されると、 nextcomplete はそれ以上、呼び出されない。
引数には、エラーの原因が渡される。

complete

完了したことを通知する。
エラーがなかった場合、最後のnextメソッドが呼び出された後に、Observableによって呼び出される。

参考: ReactiveX - Observable

Subscription

A Subscription is an object that represents a disposable resource, usually the execution of an Observable. A Subscription has one important method, unsubscribe, that takes no argument and just disposes the resource held by the subscription.

引用元 : RxJS Overview

【意訳】 Subscriptionとは、破棄可能なリソースを表し、それは大抵Observableの実行を表すオブジェクトである。
Subscriptionは、引数を取らず、subscriptionが持っているリソースの破棄を行うだけのunsubscribeという重要なメソッドを1つ持っている。

=> データストリームであるObservableシケーンスが終わりがなく、随時生成されるデータだった場合に、そのデータストリームの実行状態を終わらせるのにSubscription#unsubscribeを使用するというわけである。

subscribeを呼び出した時にその戻り値として、Subscriptionが返却される。
以下のような感じで使う。

// observable.subscribeの戻り値として、subscriptionを返却
// observable.subscribeの内部的に、Observerのオブジェクトが生成されていている。
let subscription = observable.subscribe(val => console.log(val));
// 進行中の実行をキャンセル
subscription.unsubscribe();

実際にコードを書いてみた

実際に超基礎的なコードを書いてみた。 これまでにまとめてきた概念などを理解しやすくするために、かなり簡単なコードになっているし、普通はもっと合理的な書き方をした方が良い。
それぞれの処理が何をやっているかなどの細かい説明は、コードの中にコメントとして記述してある。

実際のコード

typescript2.5.2を使用している。

import {Observable} from "rxjs";

// Observable.createは、ObservableのコンストラクタのエイリアスでObservableのオブジェクトを生成する
// 引数に、subscribe関数を渡す
// Observableはcreateで作れるけど、基本的にcreateを使って、Observableを生成することはあまりない
// ofとかの別のoperatorから生成する
let observable = Observable.create(function subscribe(observer) {
    try { // 通常時
        // データを通知し、送信する
        // 引数は、実際にObserverに送信されるデータを表している。
        observer.next(0);
        observer.next(1);
        observer.next(2);
        observer.next(3);
        observer.next(4);
        observer.next(5);

        // 正常に完了したことを通知する
        // これが呼ばれた後にnext()しても意味がない
        observer.complete();
    } catch (err) { // 異常時
        // エラーを通知する
        observer.error(err);
    }
});
console.log('開始');
// observable.subscribeの内部的に、Observerのオブジェクトが生成されている
observable.subscribe(val => console.log(val));


console.log('終了');

実行結果

開始
0
1
2
3
4
5
終了

参考

参考文献

須田智之 (2017/2/16)『RxJavaリアクティブプログラミング』 翔泳社

参考にさせていただいたサイト

ReactiveX/rxjs: A reactive programming library for JavaScript
RxJS API Document
(http://reactivex.io/documentation/observable.html))
ReactiveX - Observable
「RxJS」初心者入門 - JavaScriptの非同期処理の常識を変えるライブラリ
http://reactivex.io/rxjs/manual/overview.html#subscribing-to-observables

Qiitaでも同一の記事を投稿している。

【技術記事】Angular4(over2~)のロケールとPipe

Angular4(over2~)のロケール

ローケルとは

ロケールとは、ソフトウェアに内蔵される、言語や国・地域ごとに異なる単位、記号、日付、通貨などの表記規則の集合。または単に、利用する言語や国・地域の指定。多くのソフトウェアやプログラミング言語は、使用する言語とともにロケールを設定し、ロケールで定められた方式に基づいてデータの表記や処理を行う。

引用元 : ロケール(ロカール)とは - IT用語辞典

つまり、各国・地域などによって異なる諸々の表記方法であり、今回はこれを設定する。

ローケルとPipe

AngularのPipeでは、このローケルの設定によって、処理結果が異なるものが多々存在する。

DatePipe

DatePipeもローケルの設定によって、処理結果が変わるPipeの1つである。
例えば、ロケールを設定しないで、以下のコードを実行する。

app.component.ts

import {Component} from '@angular/core';

@Component({
  selector: 'app-root',
  template: `<p class="today">
本日は、{{today | date}} だ
</p>
`,
  styles: [`
.today{background-color:yellow}
`]
})
export class AppComponent {
  today = Date.now();
}

すると、表示される画面は以下のようになる。
スクリーンショット 2017-08-30 13.01.34.png

Angularのアプリケーションにロケールを設定する

LOCALE_IDを使用し、アプリケーションにロケールを設定する。
LOCALE_IDをprovideする際にuseValueでLocaleIdを与えると、アプリケーションのロケールがLocaleIdで渡されたものに設定される。

実際にやってみた

今回は、ロケールを日本に設定してみる。
app.module.tsのproviderでLOCALE_IDをprovideする際にuseValueで与えるLocaleIdを js-JP (日本)にする。
実際のコードは以下。
app.module.ts

import {BrowserModule} from '@angular/platform-browser';
import {NgModule, LOCALE_ID} from '@angular/core';
import {AppComponent} from './app.component';

@NgModule({
  declarations: [
    AppComponent
  ],
  imports: [
    BrowserModule],
  providers: [{provide: LOCALE_ID, useValue: 'ja-JP'}],
  bootstrap: [AppComponent]
})
export class AppModule {
}

componentは先ほどと変わらず以下のようなものにする。
app.component.ts

import {Component} from '@angular/core';

@Component({
  selector: 'app-root',
  template: `<p class="today">
本日は、{{today | date}} だ
</p>
`,
  styles: [`
.today{background-color:yellow}
`]
})
export class AppComponent {
  today = Date.now();
}

実行結果

実行すると以下のようになり、ロケールが日本になったため、同じコードでもDataPipeで変換された後の値が変化していることがわかる。

スクリーンショット 2017-08-30 13.16.31.png

参考文献

山田 祥寛(2017/8/4)『Angularアプリケーションプログラミング』 技術評論社

参考にさせていただいたサイト

ロケール(ロカール)とは - IT用語辞典 Angular - Pipes

DatePipe

LOCALE_ID

Qiitaでも同一の投稿を行っている
Angular4(over2~)のロケールとPipe - Qiita

【技術記事】Javaのシリアライズ(直列化)メモ

Javaシリアライズ(直列化)メモ

今更だが、Javaインスタンスをファイルなどに保存するのに便利なシリアライズ(直列化)についてのメモ。

シリアライズとデシリアライズ

シリアライズ(直列化)

Javaインスタンスをバイト列として出力すること。

シリアライズ

バイト列をJavaインスタンスに復元すること。

何が嬉しいのか

この機能を使うと、簡単にインスタンスを外部記憶装置などに保存し、インスタンスの情報を永続化することができる。
何か特別な処理をしたり、ある形式に沿ってインスタンスを保存したりするような面倒なことを省くことができる。

java.io.Serializableインターフェース

シリアライズを行いたいインスタンスの元となるクラスでは、java.io.Serializableインタフェースを実装する必要がある。
この、Serializableインタフェースには、フィールド及びメソッドが存在せず、ただただ、シリアライズ(直列化)可能ですよということを示すことのみを提供する。

参考 : Serializable (Java Platform SE 8 )

ObjectOutputStreamクラスとObjectInputStreamクラス

ObjectOutputStreamクラス

ObjectOutputStream は、基本データ型と Java オブジェクトのグラフを OutputStream に書き込みます。

引用元 : ObjectOutputStream (Java Platform SE 6)

=> 出力用のStreamに基本データ型とオブジェクトのグラフを書き込む

ObjectIntputStreamクラス

ObjectOutputStreamを使って作成されたプリミティブ・データとプリミティブ・オブジェクトを直列化復元します

引用元 : ObjectInputStream (Java Platform SE 8 )

=> ObjectOutputStreamを使って書き込んだものを復元する

serialVersionUID

あるクラスをシリアライズ化して、ファイルとして永続をした後、そのクラスのフィルールドなど変更したりした時に、変更後のJavaのクラスとシリアライズされたバイト列から再度でシリアライズされた際に復元されるインスタンスは、矛盾が生じる可能性がある。
そうならないために、このserialVersionUIDにバージョンとして値を与えて、コードに変更があった時には、この値を毎回変えるというふうにする。
そうすることで、復元元と復元先のこのserialVersionUIDの値が一緒だったら、同じ世代のインスタンスなので、矛盾が生じることなく、復元を行うことができると保証されるわけである。

よく、何かのシステムを作ったりする時や、プログラミング言語でもバージョニングということを行うことがあると思うが、それに近い。
例えば、Java1.8ならStream APIは存在するけど、それ未満だと存在しないよみたいな感じで。

参考 : 難解なSerializableという仕様について俺が知っていること、というか俺の理解 - 都元ダイスケ IT-PRESS

参考 : 中山 清喬(2014/9/22)『スッキリわかるJava入門 実践編 第2版 スッキリわかるシリーズ』 インプレスジャパン

実装してみた

全体の流れ

  1. Languageクラスをインスタンス科する
  2. ファイル名を元にFileOutputStreamクラスのインスタンスを生成する
  3. FileOutputStreamクラスを元に、ObjectOutputStreamクラスを生成する
  4. ObjectOutputStream.writeObjectでファイルに書き込みを行う
  5. ファイル名を元にFileInputStreamクラスのインスタンスを生成する
  6. FileInputStreamクラスを元に、ObjectInputStreamクラスを生成する
  7. ObjectInputStream.readObjectでインスタンスを復元する

図にするとこんな感じ

serialize.png

コード

Main.java

package com.company;

import java.io.*;

/**
 * メインクラス
 */
public class Main {
    /**
     * メインメソッド
     *
     * @param args コマンドライン引数
     */
    public static void main(String args[]) {
        String fileName = "lang.txt";

        // Languageクラスをインスタンス化
        Language language = new Language("Java", "Static");
        try {
            Main.Serialize(language, fileName);
        } catch (IOException e) {
            e.printStackTrace();
        }

        try {
            Language deSirializedLanguage = Main.DeSerialize(fileName);
        } catch (IOException | ClassNotFoundException e) {
            e.printStackTrace();
        }

        System.out.println("デシリアライズされた、Languageの名前は" + language.getName() + "タイプは" + language.getType());
    }

    /**
     * オブジェクトのシリアライズを行う
     *
     * @param language Languageクラスのインスタンス
     * @param fileName ファイル名
     * @throws IOException IO例外
     */
    private static void Serialize(Language language, String fileName) throws IOException {
        // わかりやすくするために冗長に書いている
        // ファイルを指定して、FileOutputStreamを生成
        FileOutputStream fileOutputStream = new FileOutputStream(fileName);
        // OutputStreamに基本データ型とオブジェクトのグラフをOutputStreamに書き込む
        // 今回は、FileOutputStreamに書き込む
        ObjectOutputStream objectOutputStream = new ObjectOutputStream(fileOutputStream);

        // 特定のクラスのオブジェクトの状態をストリームに書き込む
        objectOutputStream.writeObject(language);
        objectOutputStream.flush();

        objectOutputStream.close();
    }

    /**
     * オブジェクトのデシリアライズを行う
     *
     * @return Languageクラスのインスタンス
     */
    private static Language DeSerialize(String fileName) throws IOException, ClassNotFoundException {
        // わかりやすくするために冗長に書いている
        // ファイルを指定して、FileInputStreamを生成
        FileInputStream fileInputStream = new FileInputStream(fileName);
        // 事前にObjectOutputStreamを使って作成されたプリミティブ・データとプリミティブ・オブジェクトを直列化復元する
        ObjectInputStream objectInputStream = new ObjectInputStream(fileInputStream);

        // 対応するwriteObjectメソッドによってストリームに書き込まれたデータを使用する特定のクラスのオブジェクトの状態を読み込み、復元する
        Language language = (Language) objectInputStream.readObject();

        objectInputStream.close();

        return language;
    }
}

Language.java

package com.company;

import java.io.Serializable;

/**
 * プログラミング言語を表す
 * Serializableインターフェースを実装する
 */
public class Language implements Serializable {

    /**
     * シリアルバージョンUID
     */
    private static final long serialVersionUID = 1L;

    /**
     * 言語の名前
     */
    private String name;
    /**
     * 言語のタイプ
     */
    private String type;

    Language(String name, String type) {
        this.name = name;
        this.type = type;
    }

    /**
     * 名前
     *
     * @return nam 言語の名前
     */
    public String getName() {
        return name;
    }

    /**
     * typeのゲッター
     *
     * @return type 言語のタイプ
     */
    public String getType() {
        return type;
    }
}

実行結果

デシリアライズされた、Languageの名前はJavaタイプはStatic

参考文献

中山 清喬(2014/9/22)『スッキリわかるJava入門 実践編 第2版 スッキリわかるシリーズ』 インプレスジャパン

Serializable (Java Platform SE 8 )

ObjectOutputStream (Java Platform SE 6)

ObjectInputStream (Java Platform SE 8 )

難解なSerializableという仕様について俺が知っていること、というか俺の理解 - 都元ダイスケ IT-PRESS

【Java】Serializableの基本(シリアライズ・直列化) - TASK NOTES

オブジェクトのシリアライズとデシリアライズ - Java 入門

【Java】Serializableの基本(シリアライズ・直列化) - TASK NOTES

※ Qiitaでも同一の投稿を行っている
qiita.com

【技術記事】Python3とJavaでクラスを書いてみた

Python3とJavaでクラスを書いてみた

静的言語ばかりやってきたが、機械学習とかやりたいので、Pythonの勉強を始めることにした。
これまでやってきたGo、Java、TypeScriptとはなかなか違うところが多くてカルチャーショックを受けている...
とは言え、新しい言語を学ぶのは心が躍るものだ。

とりあえず、Pythonのclass構文を忘れないようにJavaPythonのclassを比較できるように両方の言語で同じクラスとその子クラスを書いてみた。

やっていることの説明

どちらもPhoneクラスを作成する。
次にその子クラスであるSmartPhoneクラスを作成する。
両方をインスタンス化して各々のメソッドを呼び出す。(calss methodは当然だがインスタンス化していない)

ちなみに電話とスマホを題材にしたのは、記事を書くときに手元にiphoneがあったから (笑)

細かい処理などはコード内にメモした。

まずはJavaから

Phoneクラス

package com.company;

/**
 * 電話クラス
 */
public class Phone {
    /**
     * 電話番号
     */
    private String number;

    /**
     * コンストラクタ
     * @param number
     */
    public Phone(String number) {
        this.number = number;
    }

    /**
     * 自分の電話番号を表示する
     */
    public void showMyNumber(){
        System.out.println("This phone's number is " + this.number);
    }

    /**
     * 指定された相手に電話をかける
     * @param phonePartner 電話の相手
     */
    public void call(String phonePartner) {
        System.out.println("Call to " + phonePartner);
    }
}

Smart Phoneクラス

Phoneクラスの子クラス

package com.company;

/**
 * スマホ
 * Phone classの子クラス
 */
public class SmartPhone extends Phone {
    /**
     * os
     */
    private String os;


    /**
     * コンストラクタ
     *
     * @param number 電話番号
     * @param os     os
     */
    public SmartPhone(String number, String os) {
        super(number);
        this.os = os;
    }


    /**
     * 指定された相手にTV電話をかける
     * オーバーライドした
     *
     * @param phonePartner 電話の相手
     */
    @Override
    public void call(String phonePartner) {
        System.out.println("Video call to" + phonePartner);
    }

    /**
     * OSを表示する
     */
    public void showMyOS() {
        System.out.println("his phone's os is" + this.os);
    }

    /**
     * クラスメソッド
     * 写真を取る
     *
     * @param subject 被写体
     */
    public static void takeAPitcure(String subject) {
        System.out.println("Take a picture of " + subject);
    }

}

メインクラス

ここで各々のクラスのインスタンス化を行う。

package com.company;

/**
 * メイン
 */
public class Main {
    public static void main (String[] args) {
        System.out.println("===Normal Phone===");
        // インスタンス化
        Phone normalPhone = new Phone("xxx-xxx-xxx");
        normalPhone.showMyNumber();
        normalPhone.call("sekky0905");

        System.out.println("===Smart Phone===");
        // インスタンス化
        SmartPhone iPhone = new SmartPhone("○○○-○○○-○○○", "ios");
        iPhone.showMyNumber();
        iPhone.call("sekky0905");
        iPhone.showMyOS();
        SmartPhone.takeAPitcure("flower");

        System.out.println("===Smart Phone2===");
        // インスタンス化
        Phone zenPhone = new SmartPhone("△△△-△△△-△△△", "android");
        zenPhone.showMyNumber();
        zenPhone.call("sekky0905");
        // ちなみに当然だけどこれはできない
//        zenPhone.showMyOS();

    }

}

Javaの実行結果

===Normal Phone===
This phone's number is xxx-xxx-xxx
Call to sekky0905
===Smart Phone===
This phone's number is ○○○-○○○-○○○
Video call tosekky0905
his phone's os isios
Take a picture of flower
===Smart Phone2===
This phone's number is △△△-△△△-△△△
Video call tosekky0905

続いてPythonのコード

# クラス
class Phone:
    # コンストラクタ
    def __init__(self, number):
        self.__number = number

    # Pythonのメソッドは必ず1つの引数を持つらしい
    # 第一引数は、必ずselfにするのが慣習らしい
    def show_my_number(self):
        print('This phone\'s number is{0}.'.format(self.__number))

    # Pythonのメソッドは必ず1つの引数を持つらしい
    # 第一引数は、必ずselfにするのが慣習らしい
    def call(self, phone_partner):
        print('Call to {0}.'.format(phone_partner))


# Phoneの子クラス
class SmartPhone(Phone):
    def __init__(self, number, os):
        super().__init__(number)
        self.os = os

    # Overrideした
    def call(self, phone_partner):
        print('Video call to {0}.'.format(phone_partner))

    def show_my_os(self):
        print('This phone\'s os is {0}.'.format(self.os))

    # クラスメソッド
    @classmethod
    def take_a_picture(cls, subject):
        print('Take a picture of {0}.'.format(subject))

print("===Normal Phone===")
# インスタンス化
normalPhone = Phone("xxx-xxx-xxx")
normalPhone.show_my_number()
normalPhone.call("sekky0905")

print("===Smart Phone===")
# インスタンス化
iPhone = SmartPhone("○○○-○○○-○○○", "ios")
iPhone.show_my_number()
# Phoneを継承しているから使える
iPhone.call("sekky0905")
iPhone.show_my_os()
# クラスメソッド
SmartPhone.take_a_picture("flower")

Pythonの実行結果

===Normal Phone===
This phone's number isxxx-xxx-xxx.
Call to sekky0905.
===Smart Phone===
This phone's number is○○○-○○○-○○○.
Video call to sekky0905.
This phone's os is ios.
Take a picture of flower.

感想

Pythonは最初は抵抗があったけど、慣れてきて意外に書きやすいかもと思ってきた。
でもPython触った後に静的言語を書くと故郷に帰ってきた安心感がある。

新しい言語を学ぶのは楽しい~!

参考にさせていただいたサイト

9. クラス — Python 3.6.1 ドキュメント

Python基礎講座(13 クラス) - Qiita

※ Qiitaでも同一の投稿を行っている Python3とJavaでクラスを書いてみた - Qiita

【技術記事】PythonでFizz Buzz書いてみた

PythonFizz Buzz書いてみた

静的言語ばかりやってきたが、機械学習とかやりたいので、Pythonの勉強を始めることにした。
これまでやってきたGo、Java、TypeScriptとはなかなか違うところが多くてカルチャーショックを受けている...
とは言え、新しい言語を学ぶのは心が躍るものだ。
とりあえず、基礎の基礎ということで、Fizz Buzzを書いてみた。

ルール

1~100までの数字で、
3で割り切れれば、「Fizz!」を表示する
5で割り切れれば、「Buzz!」を表示する
3と5で割り切れれば、「Fizz Buzz!」を表示する
上記以外の場合は、そのままの数字を表示する

いざコード

whileバージョン

i = 1
while i < 101:
    if i % 15 == 0:
        print("Fizz Buzz!")
    elif i % 3 == 0:
        print("Fizz!")
    elif i % 5 == 0:
        print("Buzz!")
    else:
        print(i)

    i += 1

forバージョン

for i in range(1, 101):
    if i % 15 == 0:
        print("Fizz Buzz!")
    elif i % 3 == 0:
        print("Fizz!")
    elif i % 5 == 0:
        print("Buzz!")
    else:
        print(i)

実行結果

1
2
Fizz!
4
Buzz!
Fizz!
7
8
Fizz!
Buzz!
11
Fizz!
13
14
Fizz Buzz!
16
17
Fizz!
19
Buzz!
Fizz!
22
23
Fizz!
Buzz!
26
Fizz!
28
29
Fizz Buzz!
31
32
Fizz!
34
Buzz!
Fizz!
37
38
Fizz!
Buzz!
41
Fizz!
43
44
Fizz Buzz!
46
47
Fizz!
49
Buzz!
Fizz!
52
53
Fizz!
Buzz!
56
Fizz!
58
59
Fizz Buzz!
61
62
Fizz!
64
Buzz!
Fizz!
67
68
Fizz!
Buzz!
71
Fizz!
73
74
Fizz Buzz!
76
77
Fizz!
79
Buzz!
Fizz!
82
83
Fizz!
Buzz!
86
Fizz!
88
89
Fizz Buzz!
91
92
Fizz!
94
Buzz!
Fizz!
97
98
Fizz!
Buzz!

関連記事

Go言語でFizzBuzz!を書いてみた - Qiita TypeScript(JavaScript)でアロー関数で即時関数でFizzBuzz! - Qiita

※Qiitaでも同一の投稿を行っている
PythonでFizz Buzz書いてみた - Qiita

【技術記事】Apache Beam with Google Cloud Dataflow(over 2.0.x系)入門~基本的なGroupByKey編~

Apache Beam with Google Cloud Dataflow(over 2.0.x系)入門~基本的なGroupByKey編~

Apache Beamの5つのCore Transformの内の1つ、GroupByKeyの基本的な使い方について記す。
CoGroupByKeyなどについては別の機会に書けたらなと思う。

Apache Beam や Cloud Dataflowの基本についてはこちら

公式のBeam Programming Guideを参考に書かせていただいている。

GroupByKeyとは

並列なreduction操作。
Map/Shuffle/Reduce-styleでいうところのShuffle。
GroupByKeyは、簡単に言うとその名の通り「KeyによってCollectionをGroup化する」Core Transform。
Keyは同じだが、valueが異なるペアが複数存在するKey-ValueなCollectionを結合して新しいCollectionを生成する。
共通なKeyを持っているデータを集約するのに役に立つ。

multimapとuni-map

multimap

例えば、Java, Python, GoというKeyがあったとする。
その複数の各Keyに各々Valueが数字で割り当てられている。
変換前のこのMapをmultimapという。

Java,1
Python,5
Go,1
Java,3
Java,2
Go,5
Python,2
Go,2
Go,9
Python,6

uni-map

上記のKey-ValueなmultimapのCollectionに対してGroupByKeyを適用すると以下のような結果が得られる。

Java [1, 6, 8]
Python [2, 7]
Go[7, 8]

変換後このMapをuni-mapと呼ぶ。
一意のJava, Python, GoというKeyに対して、数字のCollectionのMapが割り当てられている。

Beam SDK for Java特有のKey-Valueの表し方

Beam SDK for Javaでは、通常のJavaとは異なるKey-Valueの表し方をする。
KV<K, V>という型でkey-valueのオブジェクトを表す。

実際にコードを書いてみた

読み込むファイル

Java,1
Python,5
Go,1
Java,3
Java,2
Go,5
Python,2
Go,2
Go,9
Python,6

実際のJavaのコード

各処理は、コードにコメントとして記載している。
理解を優先するため、メソッドチェーンを極力使用していない。
そのため、冗長なコードになっている。

import org.apache.beam.sdk.Pipeline;
import org.apache.beam.sdk.io.TextIO;
import org.apache.beam.sdk.options.PipelineOptions;
import org.apache.beam.sdk.options.PipelineOptionsFactory;
import org.apache.beam.sdk.transforms.DoFn;
import org.apache.beam.sdk.transforms.GroupByKey;
import org.apache.beam.sdk.transforms.ParDo;
import org.apache.beam.sdk.values.KV;
import org.apache.beam.sdk.values.PCollection;


/**
 * メイン
 * Created by sekiguchikai on 2017/07/12.
 */
public class Main {
    /**
     * 関数オブジェクト
     * 与えられたString str, String numを","で分割し、
     * numをInteger型に変更して、KV<String, Integer>型にする
     */
    static class SplitWordsAndMakeKVFn extends DoFn<String, KV<String, Integer>> {
        @ProcessElement
        // ProcessContextは、inputを表すobject
        // 自分で定義しなくてもBeam SDKが勝手に取ってきてくれる
        public void processElement(ProcessContext c) {
            // ","で分割
            String[] words = c.element().split(",");
            // 分割したword[0]をKに、words[1]をIntegerに変換してVにする
            c.output(KV.of(words[0], Integer.parseInt(words[1])));
        }
    }

    /**
     * 関数オブジェクト
     * KV<String, Iterable<Integer>型をString型に変更する
     */
    static class TransTypeFromKVAndMakeStringFn extends DoFn<KV<String, Iterable<Integer>>, String> {
        @ProcessElement
        public void processElement(ProcessContext c) {
            // inputをString型に変換する
            c.output(String.valueOf(c.element()));

        }

    }


    /**
     * インプットデータのパス
     */
    private static final String INPUT_FILE_PATH = "./sample.txt";
    /**
     * アウトデータのパス
     */
    private static final String OUTPUT_FILE_PATH = "./result.csv";

    /**
     * メイン
     * 理解のため、メソッドチェーンを極力使用していない
     * そのため、冗長なコードになっている
     *
     * @param args 引数
     */
    public static void main(String[] args) {
        // まずPipelineに設定するOptionを作成する
        // 今回は、ローカルで起動するため、DirectRunnerを指定する
        // ローカルモードでは、DirectRunnerがすでにデフォルトになっているため、ランナーを設定する必要はない
        PipelineOptions options = PipelineOptionsFactory.create();

        // Optionを元にPipelineを生成する
        Pipeline pipeline = Pipeline.create(options);

        // inout dataを読み込んで、そこからPCollection(パイプライン内の一連のデータ)を作成する
        PCollection<String> lines = pipeline.apply(TextIO.read().from(INPUT_FILE_PATH));

        // 与えられたString str, String numを","で分割し、numをInteger型に変更して、KV<String, Integer>型にする
        PCollection<KV<String, Integer>> kvCounter = lines.apply(ParDo.of(new SplitWordsAndMakeKVFn()));

        // GroupByKeyで、{Go, [2, 9, 1, 5]}のような形にする
               // GroupByKey.<K, V>create())でGroupByKey<K, V>を生成している
        PCollection<KV<String, Iterable<Integer>>> groupedWords = kvCounter.apply(
                GroupByKey.<String, Integer>create());

        // 出力のため、<KV<String, Iterable<Integer>>>型からString型に変換している
        PCollection<String> output = groupedWords.apply(ParDo.of(new TransTypeFromKVAndMakeStringFn()));

        // 書き込む
        output.apply(TextIO.write().to(OUTPUT_FILE_PATH));

        // run : PipeLine optionで指定したRunnerで実行
        // waitUntilFinish : PipeLineが終了するまで待って、最終的な状態を返す
        pipeline.run().waitUntilFinish();
    }
}

ちなみにメソッドチェーンを使うとこんな感じ。
だいぶすっきりした。

import org.apache.beam.sdk.Pipeline;
import org.apache.beam.sdk.io.TextIO;
import org.apache.beam.sdk.options.PipelineOptionsFactory;
import org.apache.beam.sdk.transforms.DoFn;
import org.apache.beam.sdk.transforms.GroupByKey;
import org.apache.beam.sdk.transforms.ParDo;
import org.apache.beam.sdk.values.KV;


/**
 * メイン
 * Created by sekiguchikai on 2017/07/12.
 */
public class Main {
    /**
     * 関数オブジェクト
     * 与えられたString str, String numを","で分割し、
     * numをInteger型に変更して、KV<String, Integer>型にする
     */
    static class SplitWordsAndMakeKVFn extends DoFn<String, KV<String, Integer>> {
        @ProcessElement
        // ProcessContextは、inputを表すobject
        // 自分で定義しなくてもBeam SDKが勝手に取ってきてくれる
        public void processElement(ProcessContext c) {
            // ","で分割
            String[] words = c.element().split(",");
            // 分割したword[0]をKに、words[1]をIntegerに変換してVにする
            c.output(KV.of(words[0], Integer.parseInt(words[1])));
        }
    }

    /**
     * 関数オブジェクト
     * KV<String, Iterable<Integer>型をString型に変更する
     */
    static class TransTypeFromKVAndMakeStringFn extends DoFn<KV<String, Iterable<Integer>>, String> {
        @ProcessElement
        public void processElement(ProcessContext c) {
            // inputをString型に変換する
            c.output(String.valueOf(c.element()));

        }

    }


    /**
     * インプットデータのパス
     */
    private static final String INPUT_FILE_PATH = "./sample.txt";
    /**
     * アウトデータのパス
     */
    private static final String OUTPUT_FILE_PATH = "./result.csv";

    /**
     * メイン
     * 理解のため、メソッドチェーンを極力使用していない
     * そのため、冗長なコードになっている
     *
     * @param args 引数
     */
    public static void main(String[] args) {
        Pipeline pipeline = Pipeline.create(PipelineOptionsFactory.create());

        // メソッドチェーンを使った書き方
        pipeline.apply(TextIO.read().from(INPUT_FILE_PATH))
                .apply(ParDo.of(new SplitWordsAndMakeKVFn()))
                .apply(GroupByKey.<String, Integer>create())
                .apply(ParDo.of(new TransTypeFromKVAndMakeStringFn()))
                .apply(TextIO.write().to(OUTPUT_FILE_PATH));

        // run : PipeLine optionで指定したRunnerで実行
        // waitUntilFinish : PipeLineが終了するまで待って、最終的な状態を返す
        pipeline.run().waitUntilFinish();
    }
}

実行結果

以下の3つのファイルが生成される。
result.csv-00000-of-00003
result.csv-00001-of-00003
result.csv-00002-of-00003

それぞれのファイルの中身は、以下。
分散並列処理で処理が行われているので、中身が空白のファイルや、中身が1つ、2つのものがあったりとバラバラである。
また、どの内容がどのファイルに出力されるかは毎回ランダムである。

result.csv-00000-of-00003
中身なし

result.csv-00001-of-00003

KV{Java, [1, 3, 2]}

result.csv-00002-of-00003

KV{Go, [5, 2, 9, 1]}
KV{Python, [5, 2, 6]}

関連記事

Apache Beam with Cloud Dataflow(over 2.0.0系)入門~基本部分~ParDoまで~ - Qiita

IntelliJとGradleで始めるApache Beam 2.0.x with Google Cloud Dataflow - Qiita

参考にさせていただいたサイト

Beam Programming Guide

GroupByKey と結合  |  Cloud Dataflow のドキュメント  |  Google Cloud Platform

※Qiitaでも同一の投稿をしている Apache Beam with Google Cloud Dataflow(over 2.0.x系)入門~基本的なGroupByKey編~ - Qiita