オルトプラスエンジニアの日常をお伝えします!

識別子と順序付け

この記事はAltplus Advent Calendar 2017の6日目のエントリです。

こんにちは、職業Scalalian、C++愛好家の竹田です。

識別子は整数型で表現すること多いですが、整数型などをそのまま使用してしまうと、”順序付け可能である”や”配列のインデックスで使用できる”などの整数型の性質を前提にコーディングしてしまい危険です。

例えば、”順番通り”に並んでいて、ID番の要素にそのIDと関連付けられているデータを使用してコーディングをしてしまうと、様々なバグの温床になります。

#include <vector>
#include <iostream>

using ID = int;
using Probability = double;
using Image = int;

static std::vector<Probability> probabilityForEachID = {0.1, 0.2, 0.3, 0.4};
static std::vector<Image> imageForEachID = {10, 20, 30, 40};

Probability badProbability(ID id){
    return probabilityForEachID[id];
}

Image badImage(ID id) {
    return imageForEachID[id];
}

void displayImageAndProbability(Probability const& p, Image const& i){
    std::cout << "probability = " << p << " , image = " << i << std::endl;
}

int main(){
    displayImageAndProbability(badProbability(0), badImage(0));
}

このコードでは、probabilityForEachIDimageForEachIDの、i番目の要素がID iに関連するデータでなければいけないという暗黙のルールを守っている間は、想定通りに動作します。しかし、データの追加やコード中の操作でprobabilityForEachIDimageForEachIDの要素の並び順が変わってしまうと、インデックスとIDが対応しているという暗黙のルールが破られてしまい、IDに関連するデータが正確に引けなくなってしまいます。

識別子

問題の原点は、識別子に必要以上の性質を与えてしまったことにあります。Wikipediaで識別子の定義を引くと「識別子(しきべつし、英: identifier)とは、ある実体の集合の中で、特定の元を他の元から曖昧さ無く区別することを可能とする、その実体に関連する属性の集合のこと」(By Wikipedia)とあります。

識別子とは、集まりの中である個を他の個と区別するための情報です。"区別"をC++で表現するには、等価演算子が相応しいので、等価演算子をサポートして識別子を表現するクラスを導入して、どのようにコードが変わるか見てみます。

#include <unordered_map>
#include <iostream>

struct ID{
    int id;
};

bool operator==(ID lhs, ID rhs) {
    return lhs.id == rhs.id;
}

bool operator!=(ID lhs, ID rhs) {
    return !(lhs == rhs);
}

namespace std {
    // IDをunordered_mapで使用するための特殊化。
    template<>
    struct hash<ID>{
        size_t operator()(ID const& x) const
        {
            return hash<int>{}(x.id);
        }
    };
}

using Probability = double;
using Image = int;

static std::unordered_map<ID, Probability> probabilityForEachID 
= {{ID{0}, 0.1}, {ID{2}, 0.3}, {ID{1}, 0.2}, {ID{3},0.4}};
static std::unordered_map<ID, Image> imageForEachID 
= {{ID{1}, 20}, {ID{2}, 30}, {ID{3}, 40}, {ID{0}, 10}};

Probability betterProbability(ID id){
    return probabilityForEachID.at(id);
}

Image betterImage(ID id) {
    return imageForEachID.at(id);
}

void displayImageAndProbability(Probability const& p, Image const& i){
    std::cout << "probability = " << p << " , image = " << i << std::endl;
}

int main(){
    displayImageAndProbability(betterProbability(ID{0}), betterImage(ID{0}));
}

このコードでは、IDの同値関係(operator==)とHashを利用して、unordered_mapのキーにIDを利用しています。probabilityForEachIDimageForEachIDのデータの順番をシャッフルしていますが、意図通りに動作します。

unordered_mapは、Hashマップで実装されています。メモリ使用量やメモリ・アクセス速度が気になる場合は、以下のようにstd::vector<std::pair<ID, Data>>を使用することができます。ただしこの方法では、識別子の一意性などが保証できないため、同じIDに対するデータが複数ある状況になりえるので、データ用のコンテナを操作するところでは、事後条件のチェックやユニットテストを通じて、一意性を保証するようにしましょう。

またBoostが使える状況では、std::vector<std::pair>のような性質でmapのIFを提供しているflat_mapもあるので、flat_mapの使用も検討しましょう。

#include <vector>
#include <iostream>
#include <algorithm>

struct ID{
    int id;
};

bool operator==(ID lhs, ID rhs) {
    return lhs.id == rhs.id;
}

bool operator!=(ID lhs, ID rhs) {
    return !(lhs == rhs);
}

using Probability = double;
using Image = int;

static std::vector<std::pair<ID, Probability>> probabilityForEachID 
= {{ID{0}, 0.1}, {ID{2}, 0.3}, {ID{1}, 0.2}, {ID{3},0.4}};
static std::vector<std::pair<ID, Image>> imageForEachID 
= {{ID{1}, 20}, {ID{2}, 30}, {ID{3}, 40}, {ID{0}, 10}};

Probability betterProbability(ID id){
    auto const found = std::find_if(probabilityForEachID.begin(), probabilityForEachID.end(),
                [&](auto const& p){ return p.first == id; });
    if(found == probabilityForEachID.end()){
        throw std::out_of_range("Not found.");
    }
    return found->second;
}

Image betterImage(ID id) {
    auto const found = std::find_if(imageForEachID.begin(), imageForEachID.end(),
                [&](auto const& p){ return p.first == id; });
    if(found == imageForEachID.end()){
        throw std::out_of_range("Not found.");
    }
    return found->second;
}

void displayImageAndProbability(Probability const& p, Image const& i){
    std::cout << "probability = " << p << " , image = " << i << std::endl;
}

int main(){
    displayImageAndProbability(betterProbability(ID{0}), betterImage(ID{0}));
}

最後に

あるコンセプト(識別子)の表現に必要以上の性質(int型で表現して、順序付けやインデックスとしての使用できる性質)を与えてしまうと、バグの温床になることを見てきました。

自分で型を定義して適切な性質を与えることで、安全で見通し良いコードを書くことができます。

Solidityコードをデバッグする

この記事は Altplus Advent Calendar 2017 の5日目のエントリです。※Qiitaに書いた記事を転載しています。

はじめまして、オルトプラスラボの菊池(id:takuya-kikuchi)です。今年の4月からこちらで楽しく過ごしています。

最近思い出作りにSolidityを書き始めたので、開発環境の使い方のメモです。

Solidityとは

Ethereum上で動作させるスマートコントラクトを記述するための言語です。 jsっぽい?けど静的型付き言語。 Soliity言語で書いたコントラクトは、以下の手順を踏むことでEthereumネットワークにデプロイされ、他者が使用できる状態になります。

  1. コーディング
  2. コンパイル
  3. デプロイ

手順1〜2はテキストエディタとターミナルでもさほど問題ありませんが、3のデプロイ作業がなかなかに面倒です。手作業であたたかみのあるデプロイも人生には大事なのかもしれませんが、そういうところを楽させてくれるのが、Remixです。

Remix(browser-solidity)を使う

Remixは、もともとbrowser-solidtyと呼ばれていたものです。名前の通り、ブラウザ上で動作するSolidityのIDEです。github上からダウンロードすればローカルでも動かせます。

エディタ

なかなか高機能なエディタで、auto compileなんかも走らせることができます。なので、コンパイルエラーなんかがあれば常時アラートしてくれます。

https://camo.qiitausercontent.com/e89409282625c8dd7f13ae46d7fa60ba0d23b51b/68747470733a2f2f71696974612d696d6167652d73746f72652e73332e616d617a6f6e6177732e636f6d2f302f34363138342f30303361643836652d326439652d376637612d653635652d3862326437306566373961332e706e67

ContractのDebugもできる

私はなかなか脳内デバッガーの精度が低く、ステップ実行してコードの動きを確認したくなることが多々あります。Remixであればそれも可能です。

コントラクトの中身はあまり本筋と関係ありませんが、今回はこのようなコントラクトを書いてみました。 Postで気になるあの人に向けたメッセージをブロックチェーン上に刻むことができ、Readで自分宛のメッセージを読むことができます。

pragma solidity ^0.4.0;

contract SampleContract {
    address public owner;
    mapping(address => string) public messages;

    // コンストラクタ
    function SampleContract() public {
        owner = msg.sender;
    }

    // 任意のアドレスに向けたメッセージを記す。
    function Post(address dest, string message) public {
        messages[dest] = message;
    }
    
    // メッセージを読む。何も存在しなければabort
    function Read() public constant returns (string message) {
        bytes memory strMem = bytes(messages[msg.sender]);
        require(strMem.length > 0);
        return messages[msg.sender];
    }
}

デバッグ手順

1. EnvironmentをJavaScript VMに変更

画面右側の[Run]タブにて、[Environment]を[JavaScript VM]とします。 他のEnvironmentを選択しているとデバッグできませんのでご注意。

https://camo.qiitausercontent.com/286e7fc7b5ab0f7eb2de76d51c0b9303b1c44d27/68747470733a2f2f71696974612d696d6167652d73746f72652e73332e616d617a6f6e6177732e636f6d2f302f34363138342f63646665383364352d646131622d306566312d653032652d6163613465633066653339372e706e67

2. コントラクトのデプロイ

おなじく[Run]タブ内でデプロイしたいコントラクトを選択し、[Create]ボタンを押せばデプロイ完了です。

https://camo.qiitausercontent.com/f38d1dccccd6eb9746fdfca629ec17acd4a5ab6a/68747470733a2f2f71696974612d696d6167652d73746f72652e73332e616d617a6f6e6177732e636f6d2f302f34363138342f66383361353862372d393639632d613565322d343330372d6236383061656231613431312e706e67

3. コントラクトの実行

Remix上でデプロイしたコントラクトが一覧表示されていますので、こちらから任意のfunctionを選んで実行します。

https://camo.qiitausercontent.com/e820a51831490d0d87b11f2ad363bf0faf873a2d/68747470733a2f2f71696974612d696d6167652d73746f72652e73332e616d617a6f6e6177732e636f6d2f302f34363138342f31633331303834622d383437332d396533612d303163322d3633393735383463393832332e706e67

4. デバッグ

エディタ下部のコンソールに次のようにコントラクトの実行結果が表示されていますので、右側の[Debug]をクリックします。

https://camo.qiitausercontent.com/30717b7e2af96f41c0b5758095906ea712b2a36d/68747470733a2f2f71696974612d696d6167652d73746f72652e73332e616d617a6f6e6177732e636f6d2f302f34363138342f39636665646466302d383131662d353238622d313537652d3430363039636336653865652e706e67

すると、右側のタブは自動的に[Debugger]に切り替わり、デバッグ対象のfunctionのエントリポイントで自動的に一時停止します。

https://camo.qiitausercontent.com/d8c75eb667fbb122daf07a8aa3c61c836403bdd7/68747470733a2f2f71696974612d696d6167652d73746f72652e73332e616d617a6f6e6177732e636f6d2f302f34363138342f32323934326661642d663431612d653138362d373463632d6334363433383439363233332e706e67

あとは、表示されているボタンを使ってステップイン/ステップオーバー/ブレークポイントまで飛ばす・・・といった、 一般的なデバッガで可能な操作を行うことができます。

https://camo.qiitausercontent.com/26c95890f3ccb127549d0f3a0a011a5c7debf63c/68747470733a2f2f71696974612d696d6167652d73746f72652e73332e616d617a6f6e6177732e636f6d2f302f34363138342f64396564376266312d303061352d343734632d383734362d3564336266646230363665342e706e67

ローカル変数の中身も表示可能。 Watchみたいなのはない・・・のかな?

おわりに

以上、Solidityコードをババっと書いてちゃちゃっとデバッグする方法でした。PCのメモリ上にデプロイされているだけなのであまり面白みはありませんが、Solidityでどんなことができるのか、というのを知るには良いのではないかと思います。

きちんとコントラクトのデバッグができたら、Ethereumネットワークにデプロイして遊んでみましょう。(まずはプライベートネットから!)

参考

Ethereum入門

Remix

ぼくの最近の開発環境

この記事は Altplus Advent Calendar 2017 の4日目のエントリです。 ※Qiitaに書いた記事を転載しています。

 

こんにちは、オルトプラスラボの嶋田(id:cimadai)です。

 

今日は私の最近Web開発に使っているお気に入りの開発環境について紹介したいと思います。

どのような環境か?

普段Web開発をするのに使っているのが、以下のような感じです。

  • IDE:
  • IntelliJ IDEA
  • Server side:
  • Play Framework x Scala
  • Client side:
  • TypeScript with Vue.js & SCSS

では実際にそれぞれの特徴について簡単にご紹介します。

IntelliJ IDEA

www.jetbrains.com

最近身の回りでも一大ブームメントを起こしつつあるIDEです。 JetBrainというIDEばかりを作り続けている会社が作った至極の一品です。 Scalaを嗜む身としては必需品です。 IntelliJ IDEA以外にもRiderとかCLionとか使ってます。超便利。

この記事を書いている時に利用しているのは 2017.2.6 というバージョンです。

Play Framework

Play Framework - Build Modern & Scalable Web Apps with Java and Scala

Java/ScalaにおけるWeb Frameworkの決定版、といえる頼もしいやつです。 フレームワークで利用する言語をJavaかScalaで選べるので、どちらかが得意な方であればすぐに利用できます。 ちなみに私はScala版を利用しています。

この記事を書いている時に利用しているのは 2.6.7 というバージョンです。

Scala

The Scala Programming Language

Odersky教授によって生み出されたプログラミング言語です。 サーバーサイドの言語として利用しています。 Javaなんて要らなかったんや!という気持ちにさせてくれます。

この記事を書いている時に利用しているのは 2.11.11 というバージョンです。

TypeScript

www.typescriptlang.org

Microsoftが開発したJavaScriptに対して型付けを行えるAltJSの一つです。 TypsScriptで記述したプログラムをtscというコンパイラで生JSに変換して利用します。 私はこの変換はWebpackを使ってやってます。 もう型の無い言語は書けない体なので、生JSは恐ろしくて触れません。。。

SCSS

sass-lang.com

SCSS(サス)は、従来のCSSのイマイチなところをいろいろと改善してくれているものです。 基本的な構文としてはCSSのものをそのまま利用できるのですが、mixinができたりネストが できたり、変数が使えたりととっても便利に利用できるのでベターCSSとして利用しています。

Vue.js

jp.vuejs.org

Vue.jsは従来の(jQueryにありがちな)フロントエンド開発のつらみを取り去ってくれる フロントエンド用フレームワークです。 もうDOM操作を自分で各必要はありません。すべて宣言的に記述することができ、 なおかつあらゆる変数がリアクティブに実装されていますので、jQuery時代とくらべてModel-View間の実装をかなり省略することができます。 DOMちまちま組み立てるの本当に辛いですよね。

このフレームワークの中で、TypeScriptとSCSSを合わせて利用しています。

実際のサンプル

それぞれどんな感じでどう使ってるのよ?というところですが、 実際のプロジェクトから抜粋してサンプルを作ってみたので興味のある人は御覧ください。

github.com

cloneしたら sbt run だけで起動すると思います。

まとめ

便利なものをどんどん使えるようになると楽しく仕事ができますね!

それではまた別の日に!