月別アーカイブ: 2017年2月

iOS10で最低限のカメラアプリを実装

前提

要素 バージョン
xcode 8.2.1
iOS 10.2

概要

iOS10カスタムカメラ – Swift 3 on @Qiita
について、自分なりに解釈し、一行ずつ理解しながらコードを整理した

plistの設定及びストーリーボードの設定についてはリンク先と同様なので割愛

ソースコード(swift)

import UIKit
import AVFoundation

class ViewController: UIViewController, AVCapturePhotoCaptureDelegate {

  /* カメラを表示するビュー */
  @IBOutlet weak var cameraView: UIView!
  
  /* キャプチャーセッション */
  var captureSesssion: AVCaptureSession!
  
  /* カメラ */
  var stillImageOutput: AVCapturePhotoOutput?
  
  /* プレビューレイヤー */
  var previewLayer: AVCaptureVideoPreviewLayer?
  
  /* 撮影 */
  @IBAction func takeIt(_ sender: Any) {
    
    /* 撮影単位の設定情報を生成 */
    let settingsForMonitoring = AVCapturePhotoSettings()
    
    /* フラッシュの有無(オート) */
    settingsForMonitoring.flashMode = .auto
    
    /* 手ぶれ補正を有効にする(?) */
    settingsForMonitoring.isAutoStillImageStabilizationEnabled = true
    settingsForMonitoring.isHighResolutionPhotoEnabled = false
    
    /* シャッターを切る */
    stillImageOutput?.capturePhoto(with: settingsForMonitoring, delegate: self)
    
  }
  
  /* アプリ起動直前に初期化 */
  override func viewWillAppear(_ animated: Bool) {
    
    /* カメラの初期化 */
    captureSesssion = AVCaptureSession()
    stillImageOutput = AVCapturePhotoOutput()
    
    /* カメラの解像度を1920x1080に設定する */
    captureSesssion.sessionPreset = AVCaptureSessionPreset1920x1080;
    
    /* デバイスを設定(デフォルトのビデオデバイスを選択) */
    let device = AVCaptureDevice.defaultDevice(withMediaType: AVMediaTypeVideo);
    
    do {
      
      /* デバイスを取得 */
      let input = try AVCaptureDeviceInput(device: device)
      
      /* デバイスにセッションを入力可能かを検証 */
      if (captureSesssion.canAddInput(input)) {
        
        /* デバイスをセッションに入力 */
        captureSesssion.addInput(input)
        
        /* デバイスを出力可能かを検証 */
        if (captureSesssion.canAddOutput(stillImageOutput)) {
        
          /* デバイスを出力 */
          captureSesssion.addOutput(stillImageOutput)
          
          /* カメラを起動 */
          captureSesssion.startRunning()
          
          /* プレビューレイヤーを初期化 */
          previewLayer = AVCaptureVideoPreviewLayer(session: captureSesssion)
          
          /* レイヤーの引き伸ばし設定(アスペクトフィット) */
          previewLayer?.videoGravity = AVLayerVideoGravityResizeAspect
          
          /* カメラの向きを設定(縦向き) */
          previewLayer?.connection.videoOrientation = AVCaptureVideoOrientation.portrait
          
          /* サブビューにプレビューレイヤーを重ねる */
          cameraView.layer.addSublayer(previewLayer!)
          
          /* サブビュー内のプレビューレイヤーの表示位置を調整 */
          previewLayer?.position = CGPoint(x: self.cameraView.frame.width / 2, y: self.cameraView.frame.height / 2)
          previewLayer?.bounds = cameraView.frame
          
        }
      }
    } catch {
      print(error);
    }
  }
  
  /* 写真が撮影された */
  func capture(
    _ captureOutput: AVCapturePhotoOutput,
    didFinishProcessingPhotoSampleBuffer photoSampleBuffer: CMSampleBuffer?,
    previewPhotoSampleBuffer: CMSampleBuffer?,
    resolvedSettings: AVCaptureResolvedPhotoSettings,
    bracketSettings: AVCaptureBracketedStillImageSettings?,
    error: Error?) {
    
    /* unwrapし、nilじゃないことを検証 */
    if let photoSampleBuffer = photoSampleBuffer {
    
      /* 撮影データをJPEGに変換 */
      let photoData = AVCapturePhotoOutput.jpegPhotoDataRepresentation(forJPEGSampleBuffer: photoSampleBuffer, previewPhotoSampleBuffer: previewPhotoSampleBuffer)
      
      /* JPEG画像をUIImageに変換 */
      let image = UIImage(data: photoData!)
      
      /* UIImageをフォトライブラリに保存 */
      UIImageWriteToSavedPhotosAlbum(image!, nil, nil, nil)
      
      print("hoghog");
      
    }
  }
}

※ シンタックスハイライトは、swiftが対応していなかったので便宜上JavaScriptを設定

実行結果

  • Buttonをタップで撮影
  • 撮影された写真はメディアライブラリに保存

参考

xcodeで実機の利用ができない(0xE80000E2)

前提

xcode 8.2.1
iPhone 6s
iOS 10.2

問題

iPhoneを接続したMacで、xcodeで実機ビルドしようとしたところ、以下のエラーメッセージが表示される。指示に従って画面のロックを解除しても、エラーが変わらず発生し、実機ビルドできない

Please unlock your device and reattach. (0xE80000E2).

解決策

一回接続を解除してiPhoneをロックしてもう一回アンロックして、そしてアンロックした状態のままでもう一回接続し直す。

それでもダメなら下記手順を試す

  1. 設定 → 一般 → リセット
  2. 位置情報とプライバシーをリセット
  3. パスコードを入力してリセットを実行
  4. ケーブルをつなぎ直してビルド
  5. エラーメッセージが表示されなくなる

参考

「Webを支える技術」レビュー

書籍について

タイトル Webを支える技術
より良いコードを書くためのシンプルで実践的なテクニック
著者 著: 山本 陽平
発行日 2010/05/01
ページ数 376ページ
価格 2570円
Amazon https://www.amazon.co.jp/dp/4774142042
読了日 2017/02/24

メモ

  • Webの根底となる技術(HTTP/URI/HTMLなど)を、歴史を辿りながら整理した本
  • あくまで「Webを支える技術」なので、具体的なWeb開発の技術を取り扱っているわけではない
  • 言ってしまえば、この根底技術を知らなくてもウェブアプリケーションフレームワーク(WAF)などを活用すればWebシステムは作れてしまう
  • しかし、WAFが裏側でやってくれるHTTPの動きやURIの理屈を知ることは開発する上でも重要だと思う
  • REST及びRestFullという言葉を曖昧に使っていたが、改めて理解を深めることができた
  • なかなかの分厚さがあるが、やや冗長に書かれている部分も多いので、自分の興味にあわせて読み進めると良い
  • microformat/atomの項は個人的にあまり興味がなかったので流し読みしてしまった
  • というかmicroformat/atomは本筋から逸れた無いような気がする

関連用語

以下の用語をきちんと説明できないなら読んでみると良さそう

  • REST/RestFull
  • SOAP
  • URI/URL/URN
  • ステートレス/ステートフル
  • HTTPメソッド
    — GET
    — POST
    — HEAD
    — PUT
    — DELETE
    — OPTIONS
  • 主要なステータスコード
    — 200 OK
    — 201 Created
    — 301 Moved Permanently
    — 303 See Other
    — 400 Bad request
    — 401 Unauthorized
    — 403 Not Found
    — 500 Internal Server Error
    — 503 Service Unavailable
  • 主要なHTTPヘッダ
    — Date
    — Server
    — Set-Cookie
    — Cookie
    — Referer
    — User-Argent
    — Content-Length
    — Content-Language
    — Content-Type
    — Last-Modified
    — Accept
  • 条件付きリクエスト
  • 部分的リクエスト
  • セマンティックWeb
  • RSS
  • microformats
  • Atom
  • リソース指向アーキテクチャ

AngularJSの専門用語整理その2

まさかの続き(前回)

digestサイクル

  • AngularJSがデータ双方向バインディングを実現するために、UIとモデルの差異を検証するサイクル

式(Expressions)

  • Angular式とか呼ばれてる
  • {{ 式 }} の形式でビューで展開できるJavaScriptの式っぽいもの
  • JavaScriptっぽいだけで別物
  • 全ての評価はデフォルトでスコープのプロパティに対して行われるので
  • undefinedをオブジェクトとして参照できる(無論結果は全てundefined)
  • フィルタが使える

$parse

  • Angular式を関数に変換するサービス
  • 対象のスコープを指定して関数を実行できる
    var getter = $parse('user.name');
    var context = {user:{name:'sasaki'}};
    console.log(getter(context)); // sasaki
    

$watch

  • 特定のオブジェクトやプロパティの変更を監視し、変更後に任意の処理を行わせるサービス
    $scope.hoge = 'foo';
    $scope.$watch('hoge' , function(oldValue, newValue) {
      console.log('hogeの内容が' + oldValue + 'から' + newValue + 'に変わりました');
    });
    
  • 関数を指定して、その戻り値の変化も検知できる
    $scope.$watch(function() {
      return $location.path();
    }, function() {
      console.log('パスが変わりました');
    });
    
  • watchはdigestサイクルの中で定期的に行われる

$apply

  • UIとモデルの値を同期させるサービス
  • digestサイクルの中で呼び出されるが、手動で呼び出すことも可能
  • 特にjQueryなどによってUIの値を直接書き換えた場合など、digestサイクルで検知できない場合に手動で同期させる必要がある

$inject

  • AngularJSにおける依存性注入のためのサービス
    コンストラクタ’My’に対して、依存するオブジェクト$scopeを注入してコントローラを作成する

    var My = function(s) {
      s.msg = 'Hello, AngularJS!';
    };
    My.$inject = ['$scope'];
    angular.module('myApp').controller('myController', My);
    

「リーダブルコード」レビュー

書籍について

タイトル リーダブルコード
より良いコードを書くためのシンプルで実践的なテクニック
著者 著: Dustin Boswell/Trevor Foucher
訳: 角 征典
発行日 2012/06/22
ページ数 232ページ
価格 2400円
Amazon https://www.amazon.co.jp/dp/4873115655
読了日 2017/02/20

メモ

  • コード品質を高めるためのコーディングテクニックの決定版。プログラマ全員の必読書
  • 比較的難読書の多いオライリーにしては読みやすく、そして薄い
  • およそ200ページちょっとの中に大量のテクニックが含まれており、読んだだけで意識が高まった
  • 久しぶりに国外の本を読んだが、翻訳者さんの軽快でキャラの濃い翻訳のおかげで非常に読みやすい
  • コード例は、C++/Java/JavaScript/Ruby/Perl/Pythonと様々だが、言語に依存した内容は殆ど無いので、1言語以上知っていれば問題ない
  • 随所に挟まれる、アンチパターンを皮肉った風刺画のような挿絵がステキ。でもアメリカンジョークを理解できない故に挿絵の意味がわからないことも少なからずあった
  • コメントに関する考え方は個人的に合わないものが多かった。おそらく、英語を母国語とする人(及び得意な日本人)と、英語が苦手な日本人にとってはコードとコメントの関係性に対する考えが異なるんだと思う
  • 巻末の日本人による本書の解説も良かった。やはり日本人には日本人の解説が一番しっくり来る

要約とか

以降では、主に個人的に良いなと思ったものを列挙する。ただしコレだけ見ても説明が足りていないモノがほとんどなので、その場合は必要に応じて本を開くと良い

命名

  • 識別子はプログラムの最大の説明書。最適な名前をつけよう
  • 汎用的な名前(get/setなど)は避けるか使う状況を選ぶべき
  • サフィックスやプレフィックをつけて名前に付加情報を与える
  • 名前のフォーマットで情報を伝える
  • スコープが広い変数ほど、名前を長く正確につける

美しさ

  • 同じような処理は同じようなフォーマットで記述する
  • スペースを合わせたりする作業を躊躇わない。読む時間のほうが長いのだから
  • 同じようなコードは同じような順番で記述する
  • 空行による段落分けを行う

コメントすべきでないこと

  • コードからすぐに分かることをコメントにしない
  • コメントするためにコメントをしない(手段と目的の逆転)
  • コメントは識別子を補助するものではない。補助するぐらいなら識別子を変える

コメントするべきであること

  • プログラマのそのコードに対する考え
  • コードが特殊な意図で実装されていることについて
  • コードの問題点や注意すべきこと
  • 入出力のユースケース

制御フローを読みやすく

  • 比較式は、左側が変化するモノ、右側が変化しないモノ
  • ifの条件式はなるたけ肯定形を使う。if(!hoge)などは避けたい
  • 関数から早め早めにreturnする
  • ネストを浅くするように心がける

巨大な式を分割

  • 複数回登場する巨大な式は、変数で簡素な別名を付けておく

変数と読みやすさ

  • 変数のスコープは狭ければ狭いほど良い
  • 可能な限り、一つの変数には1回しか代入しない
  • 再代入するということの多くは、値の意味が変わるということ。それなら名前も変わってしかるべき

Zaim.jsをAPI ver2.xが使えるように改良してみた

前提

  • NodeによるZaimAPIの利用 の続き
  • 内部を書き換えるだけなので、基本的な使い方は上記記事と同様
  • 元のモジュール(Zaim.js)がMITライセンスなのでそれに準ずる

概要

npmでインストールできる、zaim.jsは、Zaim API ver1を前提としているため、ver2の機能を利用することができない。
そこで、zaim.jsのソースコードをベースに、API ver2を利用できるように改変する。

OAuth用URLの差し替え

OAuthで認証してzaimオブジェクトを作成する部分の、認証用URLをver2のURLに差し替え

変更前

this.client = new oauth.OAuth(
  'https://api.zaim.net/v1/auth/request',
  'https://api.zaim.net/v1/auth/access',
  this.consumerKey,
  this.consumerSecret,
  '1.0',
  this.callback,
  "HMAC-SHA1"
);

変更後

this.client = new oauth.OAuth(
  'https://api.zaim.net/v2/auth/request',
  'https://api.zaim.net/v2/auth/access',
  this.consumerKey,
  this.consumerSecret,
  '1.0',
  this.callback,
  "HMAC-SHA1"
);

リクエストトークン取得用のURLを差し替える

変更前

this.client.getOAuthRequestToken(function(err, token, secret) {
  that.token = token;
  that.secret = secret;
  callback('https://www.zaim.net/users/auth?oauth_token=' + that.token);
});

変更後

this.client.getOAuthRequestToken(function(err, token, secret) {
  that.token = token;
  that.secret = secret;
  callback('https://auth.zaim.net/users/auth?oauth_token=' + that.token);
});

入力データ取得用APIのURLを差し替える

ver1ではPOSTだったが、ver2ではGETになるため、それについても書き換える
変更前

Zaim.prototype.getMoney = function(params, callback) {
  var url = "https://api.zaim.net/v1/money/index.json";
  if (arguments.length === 1) {
    callback = params;
    params = {};
  }
  this._httpPost(url, params, callback);
};

変更後

Zaim.prototype.getMoney = function(params, callback) {
  var url = "https://api.zaim.net/v2/home/money";
  if (arguments.length === 1) {
    callback = params;
    params = {};
  }
  this._httpGet(url, params, callback);
};

httpGetメソッドで、パラメータを付与できるように改変する

オリジナルのhttpGetメソッドは、パラメータの付与に対応していなかったので、対応できるように修正する
変更前

Zaim.prototype._httpGet = function(url, callback) {
  if (!this.token || !this.secret) {
    throw new Error("accessToken and tokenSecret must be configured.");
  }
  this.client.get(url, this.token, this.secret, function(err, data) {
    if (err) {
      throw err;
    } else {
      callback(typeof data === 'string' ? JSON.parse(data) : data);
    }
  });
};

変更後

Zaim.prototype._httpGet = function(url, params , callback) {
  url += '?';
  Object.keys(params).forEach(function(key) {
    url += key + '=' + params[key] + '&';
  });
  if (!this.token || !this.secret) {
    throw new Error("accessToken and tokenSecret must be configured.");
  }
  this.client.get(url, this.token, this.secret, function(err, data) {
    if (err) {
      throw err;
    } else {
      callback(typeof data === 'string' ? JSON.parse(data) : data);
    }
  });
};

Zaim API ver2特有の機能を使ってみる

ここまでで、getMoneyメソッドに関してはver2のAPIを呼び出せるようになったので、ver2でしか利用できない機能を使ってみる。
下記コードは、2017年にAmazonで購入した商品の一覧を取得する。

ソースコード

zaim.getMoney({
  place: 'Amazon',
  start_date: '2017-01-01'
} , function(data) {
  console.log(data.money.map(function(m) {
    return [m.date , m.place , m.amount , m.comment];
  }));
});

実行結果

$ node zaim.js
[ [ '2017-02-12', 'Amazon', 1976, 'コーディングを支える技術' ],
  [ '2017-02-04', 'amazon', 2597, 'かじり木、チモシー、ペレット、消臭、砂' ],
  [ '2017-02-04', 'Amazon', 702, 'リンゴ酢' ],
  [ '2017-02-01', 'Amazon', 980, 'Kindle Unlimited 201702' ],
  [ '2017-01-14', 'Amazon', 1734, '野菜ジュース12本セット' ],
  [ '2017-01-09', 'Amazon', 258, '応用情報技術者の本中古' ],
  [ '2017-01-04', 'Amazon', 2880, 'データベーススペシャリスト教科書' ],
  [ '2017-01-01', 'Amazon', 18820, 'FIRE HDとそのカバーとその保証' ],
  [ '2017-01-01', 'Amazon', 980, 'Kindle Unlimited 201701' ] ]
$

購入店舗に関する情報はver2から追加された機能で、ver1ではできなかった、店舗によるフィルタリングができたことがわかる。また、ver1では支出額は負数だったが、ver2では収支に関わらず正数となっている。

また、上記実行結果には関係ないが、ver1では最大取得件数が100件だったのに対し、ver2では無制限となっており汎用性も高まっている。

他のメソッドについて

同様に他のメソッドについても、APIのURLをver1のURLからver2のURLに差し替えることで使用可能となるが、
個人的にgetMoneyメソッドさえ使えれば今は困らないのでコレ以上の改変は必要に応じて行うことにする。

参考

NodeによるZaimAPIの利用 と同様

NodeによるZaimAPIの利用

本記事で使用したZaim.jsは、最終更新が2013年で、最新のAPIを利用することができません。そのため、Zaim.jsのソースコードを直接編集し、最新のAPIを利用できるように書き換えたものを利用しています。

書き換えについては、Zaim.jsをAPI ver2.xが使えるように改良してみたをご参考ください

前提

  • node/npmが使える状態
  • Zaimのアカウントを保有しており、最低限の利用記録がある状態
  • Zaim API用のカスタマーキー、アクセストークンを何らかの手段(OAuth)で取得している状態

かなり限定的な状態が対象なので限りなく個人備忘録用

概要

クラウド型の家計簿サービスであるZaimを、Node上でAPI経由で読み書きする。
本記事ではZaim API用のカスタマーキー及びアクセストークンを取得済みで、APIを利用できる状態にあることを前提とする

Zaimとは

Zaimは、おそらく国内シェアナンバー1のクラウド型家計簿サービスであり、Web/iOS/Androidそれぞれから利用することができる。他の家計簿ソフトと同様に、会計簿の記入、集計、検索、予算管理など、家計簿サービスとしての基本的な機能を備えている他、以下の特徴的な機能を持っている。

  • レシートを撮影することで全自動で入力が行われる。精度はお察し
  • 各金融機関と連携し、入出金を自動入力。金融機関の認証情報を民間企業に渡すとかどうなんだろ
  • 年末調整とかそれなりに自動でやってくれる。正確に家計簿を記入していないと無意味

などと特徴的な機能はあんまり使いものにならないので、私は普通に家計簿の入出力にしか使っていない。(※上記は私的意見です)

Zaim API とは

OAuthにてZaimのアカウントの認証を受け、API経由で家計簿のCRUDを行うことができる。

Zaim APIはRESTFULLに実装されているので、そのまま利用することも簡単であるが、今回はZaim APIをラッピングしたNodeモジュールであるZaim.jsを利用する。

Zaim.jsのインストール

Nodeモジュールなのでnpmを用いてインストール

npm install zaim -D

Zaimオブジェクトの作成

なんらかの手段で取得したカスタマーキーとアクセスキーを指定して、Zaimオブジェクトを作成する

var Zaim = require('zaim');

var zaim = new Zaim({
  consumerKey: 'hogehoge',
  consumerSecret: 'fugafuga',
  accessToken: 'foofoo',
  accessTokenSecret: 'barbar',
});

とりあえず入力履歴を取得

getMoneyメソッドで入力履歴を取得。デフォルトでは最新20件を取得するので、とりあえず2/17の記録を全て取得する。
ちょっと恥ずかしいが、実行結果は生データ。

ソースコード

zaim.getMoney({
  start_date: '2017-02-17',
  end_date: '2017-02-17'
} , function(data , err) {
  console.log(data.money);
});

実行結果

$ node zaim.js
[ { id: 1581817347,
    user_id: 3061609,
    date: '2017-02-17',
    category_id: '101',
    genre_id: '10105',
    comment: 'サラダ、ドレッシング、卵とじ丼',
    name: '',
    active: 1,
    created: '2017-02-17 19:43:22',
    currency_code: 'JPY',
    type: 'pay',
    price: '-341' },
  { id: 1581291695,
    user_id: 3061609,
    date: '2017-02-17',
    category_id: '101',
    genre_id: '10104',
    comment: '月見大盛り',
    name: '',
    active: 1,
    created: '2017-02-17 14:06:50',
    currency_code: 'JPY',
    type: 'pay',
    price: '-350' } ]

入力データの構成

前項では、2/17の記録2件の入力データを取得した。入力データはオブジェクト形式で取得でき、以下の構成になっている。

キー 内容
id 入力ID(ユニーク)
user_id ユーザID(ユニーク)
date 記録日時
category_id カテゴリ(食費/日用雑貨などの大分類)のID
genre_id ジャンル(朝食/昼食/夕食などの小分類)のID
comment 記録に対する自由記入欄(購入したモノなどを記入する)
name 不明(外部連携とかで使う??)
active 多分論理削除すると0になる
created データの登録日
currency_code お金の種類
type 支出(pay) or 収入(income)
price 金額(支出の場合負数)

特定の入力データを取得する

ここでは、2016年に「ゲーム」で使った金額の一覧を取得する

ジャンル一覧を取得

Zaimでは、「ゲーム」は、「エンターテイメント」カテゴリに属するジャンルとしてデフォルトで登録されている。
Zaim.jsでは、genre_idで指定する必要があるので、「ゲーム」のジャンルIDがいくつなのかを調べるために、以下のコードで支出のジャンル一覧を取得する。

ソースコード

zaim.getPayGenres({
  lang: 'ja',
} , function(data) {
  console.log(data);

実行結果

$ node zaim.js
{ genres:
   [ { id: 10101, category_id: 101, title: '食料品' },
     { id: 10102, category_id: 101, title: 'カフェ' },
     { id: 10103, category_id: 101, title: '朝ご飯' },
     { id: 10104, category_id: 101, title: '昼ご飯' },
     { id: 10105, category_id: 101, title: '晩ご飯' },
     { id: 10199, category_id: 101, title: 'その他' },
(中略)
     { id: 9047786, category_id: 108, title: 'ゲーム' },
(以下省略)

ゲームの支出履歴を取得

「ゲーム」のジャンルIDが9047786であることがわかったので、これを指定して2016年のゲームの支出一覧を取得する。
全部出力してしまうと長いので、価格とコメントのみmapして出力する。

ソースコード

zaim.getMoney({
  start_date: '2016-01-01',
  end_date: '2016-12-31',
  genre_id: '9047786',
} , function(data , err) {
  console.log(data.money.map(function(m) { return [m.price , m.comment]}));
});

実行結果

$ node zaim.js
[ [ '-1080', '桃鉄' ],
  [ '-1200', 'ポケモンGO' ],
  [ '-1200', 'ポケモンGO' ],
  [ '-1200', 'ポケモンGO' ],
  [ '-600', 'ポケモンGO' ],
  [ '-600', 'ポケモンGO' ],
  [ '-600', 'ポケモンGO' ],
  [ '-950', 'ポケモン空の探検隊' ],
  [ '-706', 'マジカルバケーション' ] ]

ポケモンGOにほどよく課金していたのがわかる。

参考

「コーディングを支える技術」レビュー

書籍について

タイトル コーディングを支える技術
成り立ちから学ぶプログラミング作法
著者 西尾 泰和
発行日 2013/05/25
ページ数 247ページ
価格 2470円
Amazon https://www.amazon.co.jp/dp/477415654X/
読了日 2017/02/14

メモ

  • プログラミング言語の構成要素(演算子、条件分岐、関数など)について、「何故生まれたのか」「それは何を目的としているのか」「どんな種類があるか」を体系的にまとめた書籍
  • プログラミング言語の誕生から現在までの歴史に関する読み物としても面白い
  • コード例はC++/Java/Ruby/Perl/Python/JavaScriptに加え、それらの先祖である言語まで多岐にわたる
  • と言っても、それぞれの言語を知らずとも雰囲気で理解できる上、コードに関する説明も十分されているので問題ない
  • 読み物として面白いと思ったが、これを読むことで一気にコーディングの技量を高められるというわけではない
  • 言語の構成要素の目的を知ることで、常に適した手段で実装できるようになるためのキッカケ程度にはなるかも
  • 個人的にはプログラマの必読本とまでは思わない。言語に興味関心があれば読んでみようといった感じ

要約とか感想とか

演算子

プログラミングにおける文法とは一体何か。以下の演算子に関する文法を例に、プログラミング言語の歴史について触れる。
– 中置記法(1 + 2)
– 後置記法(1 2 +)
– 前置記法(+ 1 2)

条件分岐

アセンブリ時代の条件分岐方法を元に、何故if文のような分岐命令が必要になったのか、C言語のif分はどのようにアセンブリに変換されるのかについて考える

繰り返し

アセンブリには繰り返しに関する命令はなく、C言語で言うgoto文と条件分岐を組みわせて繰り返しを実現している。それの問題点と、そこからwhileやfor文がどのように生まれたのかについて

関数

ひとまとまりの、再利用性のある処理をどのように記述するのか、関数を呼び出した後、どのように呼び出し元に戻ってくるのか

例外処理

例外処理の歴史、何故必要になるのか、言語ごとの例外処理に対する考え、実装及びそれぞれの問題点

識別子

データに対する名前付けはどのように行われているのか、何故名前付けが必要になったのか

スコープ

名前の影響範囲について。名前が常にグローバルな世界、動的スコープと静的スコープの違いなど

数値

コンピュータでは数値をどのように扱っているのか。固定小数点数と浮動小数点数など

データは全て0と1で構成されるが、同じバイト列でも型によってそれがどのようなデータなのかが異なる。型とは何か、データと合わせてどのように型を管理しているのか

配列/リスト

ひとまとまりのデータはどのように表現されているのか。配列とリストの違い及びそれぞれに対するデータの削除、追加時の計算量など

文字

文字コードの世界について。文字コードとは何なのか、何故文字コードは複数存在するのか。ビット列から文字への変換方法など

文字列

言語による文字列の表現方法の違い

並行処理

一つのCPUで複数の処理を同時に行う原理。リソースの競合に対する対策など

オブジェクト指向

そもそも「オブジェクト指向」とはなんなのか、オブジェクト指向の先祖であるSimuraから影響を受けたC++とSmallTalkでは、オブジェクト指向の定義がまったく違う。

継承

継承に関する言語ごとの様々な考え方。多重継承を許すのか、インタフェースやMIX-INによる擬似的な多重継承など

「プリンシプル オブ プログラミング」 レビュー

書籍について

タイトル プリンシプル オブ プログラミング
3年目までにみにつけたい一生役立つ101の原則原理
著者 上田 勲
発行日 2016/03/29
ページ数 303ページ
価格 2200円
Amazon https://www.amazon.co.jp/dp/4798046140/
読了日 2017/02/14

感想とかメモ

  • プリンシプル = 原則/原理
  • 良いコード(特に保守性、拡張性の優れた)コードを書くための原則、原理を集約した一冊
  • サブタイトルに「3年目まで」と書かれていたり、帯に「めざせ、脱・初心者」とアオリ文が書いてあったり、一件初心者向けの本にも見られるが、全体的に内容は難しめ
  • まるで洋書を訳したかのような言葉の選び方や言い回しが多く、それに加えて内容も難しいため、気軽に読むのが難しい項目も少なからずあった
  • 「3年目」とか関係なく、全てのプログラマが持つべき考え方が詰まっている
  • 逆に「3年目」程度では理解できない内容も多いため、経験を重ねる度に読み返すことで自分の立ち位置が把握できそう
  • 具体的なコードは一切掲載されていない。著者曰く「具体的な場面でしか使えないものと誤解されないように」とのことだが、具体的なコードが無いとそれはそれでイメージができない項目も多かった(コードは自分で書いてみようということか)
  • 当然特定の言語、環境に依存した内容ではない
  • 全体的にテストが整備されてる前提だと感じた。テストが整備されていない時点でキレイなコードを書くのは不可能なのか

要約とか備忘録

本項では、本書に記された101の原則・原理のうち、個人的に気に入ったものについて要約及び個人的な考えなどを整理する。基本的に最小限しか書いていないが、ページ数も併記しているので、気になった所だけでも読んでみよう。

1.2 コードは設計書である(P23)

  • 製造業に置ける「製造」は、システム開発におけるコーディングであるという考えは間違え
  • システムはソースコードという「設計図」に基づいて、コンパイラやビルドツールが「製造」する
  • 故にプログラミングは仕様を符号化する作業ではなく、設計という創造的行為である

1.3 コードは必ず変更される(P26)

  • 一度書いて終わるコードなどまず無い
  • コードが変更されるという事実を、コーディングする上での最重要事項として認識する必要がある。
  • 変更されることを意識すれば変更に強いコードが書ける
  • 変更に強いということは変更しやすいということであり、それは「読みやすいコード」であることだ。

2.4 PIE(意図を表現してプログラミングせよ)(P43)

  • 前提として、ソースコードはコンピュータが読むものではなく人間が読むものである
  • ソースコードは書く時間よりも読む時間のほうが圧倒的に長い
  • 一人でコードを書いたとしても、書いた直後時自ら読むことは避けられない。そしてそれは最早他人である。
  • ソースコードは、What/Howは記述されているが、Whyは記述されていない
  • つまりコメントに必要なのはWhatでもHowでも無く、Whyなのだ
  • WhatやHowをコメントで記述しなければ理解できないソースコードは設計、実装に誤りがある

2.7 名前重要(P57)

  • プログラミングにおいて、「命名」は最重要課題の一つである。
  • 名前はコードを読む人にとってのUIである
  • 名前が適切であれば、コードを読む人は名前から内容を推測することで、内容を読まずともコードリーディングを進めることができる。
  • 逆に名前が曖昧であったり、意味を成していない場合、内容まで追わなければコード全体を把握することができない。
  • 適切な名前がすぐに思いつかないのであれば、それは設計、実装に誤りがある

3.6 繰り返しの最小化(P73)

  • コード中に同じコードが繰り返し登場してはならない
  • 繰り返しのコードは、修正範囲を誇大化させる
  • さらに、繰り返し存在するコードそれぞれに同じ修正を適用して問題ないかの判断も必要になる
  • 繰り返しを排除するためには、コードを小さい部分に分割することを意識付ける。大きなコードは繰り返しが発生しても気づきにくくなるからだ

単一責任の原則(P79)

  • モジュールを変更する理由は2つ以上あってはならない
  • 理由が複数あるということは、そのモジュールが複数の責任(役割)を持っているからだ
  • モジュールは1つの1つのみの責任を持たせなければならない
  • 何故なら、2つの責任がある場合、片方の修正のためにもう片方が影響を受ける可能性があるからだ

3.18 ポリシーと実装の分離(P93)

  • ポリシー = ソフトウェアの前提に依存するビジネスロジックなど
  • 実装(メカニズム) = ソフトウェアに依存しない、再利用が可能なロジック
  • モジュール内でポリシーと実装が混在していると、ポリシーの変更に実装が影響を受けてしまうため
  • ポリシーが独立したモジュールである場合、それは再利用可能であり、単体テストも行いやすい良いモジュールである

3.20 参照の一点性(P97)

  • 定義は極力一度きりにする(再代入しない)
  • ロジックの流れを追うごとに、変数の中身が変わることは、コードを読む上での脳への負荷となりやすい
  • 単一代入を強制する言語、アーキテクチャも多く存在する

3.39 明確性の原則(P132)

  • コードの解読を3回以上繰り返してはいけない
  • 1回目は仕方ない
  • 2回目解読する機会が来たら、その場で改善しなければならない
  • 3回目を迎えることのないように改善する

3.44 透明性の原則(P132)

  • 随所にデバッグを簡単に行うための仕組みを入れておく
  • プログラマがその気になれば、どんなデバッグ情報もすぐに出せるようにする
  • 本番コードにデバッグ機能を搭載することを躊躇しない(もちろんON/OFFは切替可能に)

3.52 最適化の原則(P154)

  • 「速いコード」より「正しいコード」
  • 早い段階で「速いコード」を書こうとすると破綻する
  • まずは正しく読みやすいコード、テストを書き、ボトルネックと認められた場合に手を加える

3.58 即行プロトタイプ(P164)

  • できるだけ早くプロトタイプの開発に着手する
  • どんなに仕様を詰めても、ドキュメントを整備しても、動くものを見たら全てが変わる

3.62 シェルスクリプト活用(P173)

  • 繰り返し行なっている操作は可能な限り自動化する
  • シェルスクリプトは移植性が高い
  • シェルスクリプトはプラットフォームに依存しにくい
  • シェルスクリプトは他のソフトウェアと結合しやすい

4.5 コードの臭い(P205)

  • コードの臭い = コードから見られる理解しにくさ、修正しにくさ、拡張子に草
  • プログラマはコードの臭いに敏感である必要がある
  • いくら良いコードが書けるようになっても、悪いコードを見つける力が無ければリファクタリングはできない

5.1 プログラマの3代美徳(P214)

  • 怠慢
  • 短気
  • 傲慢

5.2 ボーイスカウトの規則(P218)

  • ボーイスカウトの規則 = ボーイスカウトは、自分のいた場所を自分が来る前よりも綺麗にして帰らなければならない
  • プログラマがソースコードを修正する時、その周辺のコードも綺麗にする必要がある

5.4 エゴレスプログラミング(P226)

  • 能力、年齢、立場に関係なく、皆でより良い物を作ることに価値を置く
  • コードを私物化せず、定期的に同僚にコードを見せたり、同僚のコードを見たりする

6.5 ラバーダッキング(P253)

  • 自分のコードについて声に出して説明すること
  • 説明相手は最悪無機物(ラバーダッキング=ゴムのアヒル)でも良い
  • 声に出して説明することで、自分が自分のコードをどれだけ理解できるかを図ることができる

PHPでタイマー制御

【内容】
特定の期間内、処理を変更する。

【用途】
稼働中のサービスでメンテナンスを行う時や、
深夜等の業務時間外に処理を変更したい場合。

//規定期間開始の年月日・時間
$s_time_stamp = mktime(15,0,0,12,30,2016);
//規定期間終了の年月日・時間
$e_time_stamp = mktime(10,0,0,1,4,2017);
//現在時間の取得
$now_time = time();
//2016年12月30日 15時0分0秒~2017年1月4日 10時0分0秒の間、true側の処理を行う
if ($now_time >= $s_time_stamp && $now_time <= $e_time_stamp) {
  //規定期間内の処理

}else{
  //規定期間外の処理

}

ほぼ同じ内容で、トリガーとする日時以降か以前かで処理を分ける場合は下記の通り。

//規定時間を設定(下記の場合は、2017年2月20日 0時0分0秒以降の処理)
$trigger_time_stamp = mktime(0,0,0,2,20,2017);
//現在時間を設定
$now_time = time();
if ($now_time >= $trigger_time_stamp) {
  //規定時間後の処理

}else{
  //規定時間前の処理

}