@tako_programingの忘備録とか

@tako_programingの忘備録とかです。技術系の話が多くなるのかも。

Java初心者にScalaを勧める記事

LiTでJavaに慣れた人へScalaのススメ

LiTで、Androidコースやマインクラフトコースをやった人は必然的にJavaを触ることでしょう。そしてJavaを書いてる途中で色々と思うことがあるんじゃないでしょうか。めんどくせえな、と。行末にセミコロンを必ずつけなくてはいけないとかインスタンスを生成するときにコンストラクタの引数がなくても括弧をつけなくちゃいけないとかListのheadとtailを取ろうとするとクソめんどくさいとか関数合成がめんどくさいとか。そんな悩みをある程度解決してくれるのがScalaという言語。

Scalaって?

Java仮想環境上で動作する強い静的型付けの関数型オブジェクト指向プログラミング言語です。Javaに比べて簡潔で、関数型的な記述が可能です。また、Javaと同じオブジェクト指向言語でもありますし、それっぽい書き方がどうしてもできない時はJavaのような命令型っぽい書き方もできます。

環境構築から

OS Xならhomebrewですぐ入ります。

$ brew update
$ brew install scala
$ brew install sbt

3行目でインストールしているsbtというのはScalaのビルドツール(JavaでいうMavenとかGradleとかみたいな)です。sbtではJavaも使えます。また、pluginを入れることでKotlinやfrege等他の言語も動くみたいです(確認はしてません)。

REPL

ScalaにはREPL(Read-Eval-Print-Loop)があります。REPLは、ターミナルで

$ scala

と入力するだけで起動します。さっそく使ってみましょう。

scala> 1 + 2

と入力してみてください。

res0: Int = 3

と出力されるはずです。これの動きは見たまんまなのでわかりますね。1と2を足してるんです。こんな感じでREPLはちょっとしたコードの動きを確かめたりするのに向いています。とりあえず今回は、このREPLを使ってScalaに軽く触れてみましょう。

変数

Scalaでは変数の宣言と代入はこのように行います。

val n: Int = 3

これはJavaだと

final int n = 3

このようになります。Scalaではval or var+ 変数名+:+型名+=+という形で宣言と代入ができます。ここでまず意味がわからないのはvalでしょう。その後のJavaの例でわかったかもしれませんが、valには宣言した変数が再代入不可になるという意味があります。試しにvalで宣言した変数に再代入してみましょう。

scala> val n: Int = 3
n: Int = 3
scala> n = 4
<console>:12: error: reassignment to val
       n = 4
         ^

エラーが起きていますね。再代入不可だからです。ここで、もう一つ不思議に思った点があるんじゃないでしょうか。なんでこんな序盤でJavaではあまり使わないfinal修飾子のようなものを例として使ってきたかという点です。Scalaでは基本的に変数の再代入を行いません。valの代わりにvarを使うことで、再代入を許すこともできますが、valを使う方が推奨されています。これはなぜかというと、再代入が可能だと状態の管理が難しくなってしまうからです。コードの中のどこで変数の中の値が変わっているかわからない状態というのはとても恐ろしいです。思わぬ例外を引き起こす可能性があります。例えばどこかでnullでも入ってたらJavaを書いたことがある人ならよく目にするであろうjava.lang.NullPointerExceptionが連発することでしょう。しかもIDEは例外が起こった場所は示してくれるかもしれませんが、変数を再代入した場所はしめしてくれないでしょう。そういった理由でScalaでは変数は極力valで宣言し、再代入を禁止することが推奨されているのです。

関数

次は関数です。ではまず簡単な関数を1つ書いてみましょう。

scala> def add3 (x: Int): Int = x + 3

この関数は引数として整数を1つとりそれに3を足して返す関数です。Scalaで関数を定義する時はdef+関数名+引数リスト+:+返り値型+=+処理となります。ではこの関数を使ってみましょう。

scala> add3(1)
res0: Int = 4

Scalaで関数を使う時はJavaと同じように関数名の括弧の中に引数をいれます。ちなみに引数のない関数は括弧を省略できます。

scala> def printHello ():Unit = println("HELLO")
printHello: ()Unit

scala> printHello
HELLO

ここで、関数の返り値に指定されてるUnitというのはJavaでいうvoidのようなものです。次は複数の引数を持つ関数を定義しましょう。基本的にはなにも変わらないです。,で引数を区切るだけです。

scala> def add (x: Int, y: Int): Int = x + y
add: (x: Int, y: Int)Int

scala> add (1, 3)
res2: Int = 4

こんな感じです。ちなみに関数が複数行にまたがる時は波括弧で囲います。

def add(x: Int, y: Int): Int = {
  x + y
}

ここまできてJavaプログラマーは「あれ?」と思った所があると思います。そうです、returnがないんです。Scalaでは関数の最後の結果が自動的に返り値になります。もちろんreturnで明示的に返り値を指定することもできますが、普通はつけなくてよい所ではreturnは付けないようにします。

関数の変数

Scalaでは、関数を第一級オブジェクト(ファーストクラスオブジェクト)として扱っているので、変数への代入が可能です。

scala> def add3 (x: Int): Int = x + 3
scala> val f: Function[Int, Int] = add3 _

これで、変数fにadd3が代入されました。実行してみましょう。

println(f(3)) //=> 6

6が出力されると思います。ちなみにScalaではFunction0~Function22というトレイト(Javaでいうインターフェース)が用意されているので、22を超える引数の関数を代入しようとするとエラーが起こります。また、関数を変数に代入する時には_が必須です。Scalaでは関数が普通のオブジェクトなので、他の関数の引数に関数を渡せたり、関数が関数の返り値にできたりします。このような関数をScalaに限った話ではないですが、高階関数と呼びます。

関数の部分適用

Scalaでは2つより多い引数を持つ関数にその引数の数より少ない数の引数を与えると、残りの引数を引数にとる関数を返す機能があります。何を言ってるのかわからないですね。下の例をみてください。

scala> def add (x: Int, y: Int): Int = x + y

scala> val f = add(3, _: Int)
  f: Int => Int = <function1>

scala> f(4)
  res0: Int = 7

これには関数の部分適用が使われています。まず3行目でadd関数に3と_プレースホルダー)を与えます。プレースホルダーは未確定の変数を意味します。すると機能的には、先程から使っているadd3関数と同等の関数がfに代入されます。そしてfは普通の関数のように残りの引数を与えて呼び出すことができます。

関数合成

ScalaではJavaと同様に関数合成をすることができます。

scala> def add3(x: Int): Int = x + 3

scala> def add5(x: Int): Int = x + 5

scala> add3(add5(1))
  res0: Int = 9

scala> def f (x: Int): Int = add3(add5(x))

愚直に書くとこうなります。意味はわかりやすいと思います。add5に1を渡して6が返ってきますね、その6をadd3に渡すと9が返ってきます。それをそのまま1つの関数にすることもできます。でも次は少しスマートに書いてみましょう。それにはFunctionオブジェクトのcomposeメソッドを使います。

scala> val add8 = (add3 _) compose (add5 _)

scala> println(add8(0))
8
Function<Int, Int> add3 = x -> x + 3
Function<Int, Int> add5 = x -> x + 5
Function<Int, Int> add8 = add3.andThen(add5)

このすぐ上のコードは(わかるとは思いますが)Javaで同じことをしたものです。可読性で言えば大差ないかもしれないですが、Scalaの場合はComsumerやPredicateのようないくつかの標準関数型インターフェースがあるわけではなく言語自体が関数をオブジェクトとしてサポートしているので、複雑になっていけばより(Javaと比較して)コードが読みやすいものになってくると思います。あと今回は両方とも足し算するだけの関数なので順番はあまり関係ないのですが、順番が関係ある場合、composeメソッドだと呼び出すときに適用する順番と逆の順番で書くことになると思います。それを避けて順序どおりに書きたいときは(JavaのFunction同様に)andThenメソッドをcomposeの時と同じようにに使うといいです。

再帰関数

話を少し変数の所に戻します。先程も言ったとおりScalaの変数は基本的にvalを使います。じゃあこんなJavaArrayListの総和を求めるコードをScalaでvarを使わずに書きたいときはどうしますか?

public int accumulate( ArrayList<Integer> arrayListA ){
        int sum = 0;
        for( Integer tmp : arrayListA )
            sum += tmp;
  
        return sum;
}

少し悩むと思います。そしてここで登場するのが再帰関数です。再帰関数とは関数の中で自分を呼ぶ関数のことです。さきほどの配列の総和を求める例で見てみましょう。

def total(List[Int]: xs): Int = if (xs.isEmpty) 0 else xs.head + total(xs.tail)

あら簡単。一行で書けちゃいました。ちなみにxs.headでListの先頭要素を、xs.tailで、Listの先頭以外の要素を取得します(これ自体もJavaでやろうとすると結構めんどくさいですよね)。このように再帰関数を使うと簡単に綺麗に関数が書けるようになります。

終わりに

今回はJavaとくらべてScalaが良いぞって話だったので、Javaと書き方に大きな差のない基本構文(ifなど)は紹介しませんでした。なので、この記事を読んでいきなりScala書けやと言われても到底ムリな話です。ですが、この記事を読んだLiT出身のJavaプログラマーJavaの、主に関数周りの煩わしさから開放される1つの手段としてScalaのことを考えてくれるようになったのであればそれは自分にとってすごく嬉しいことです。Scalaは今は主にPlayFrameWorkやScalatraといったフレームワークWebのサーバーサイドで使われている言語ですが、Androidアプリ開発もできないことはありませんし、Scala.jsというライブラリでJavaScriptコンパイルができるのでAltJSとしてWebフロントエンドの開発に貢献できるようになるかもしれません。(Androidアプリ開発をするならKotlinという言語を断然オススメします。)

AWS Mobile Analyticsのイントロダクションとかをてきとうに和訳

バイト先でAWS Mobile Analyticsを使うことになって調べていたのを英語が読めないので和訳したのでメモ的に貼っておきます。

AWS Mobile Analytics

公式のGetting Started

  • 1 : AWSアカウントでサインアップする

  • 2 : 以下の手順に従ってアプリを追加する。

    • 次のリンクからAmazon Mobile Analyticsコンソールを開きます
      Amazon Mobile Analytics Console
    • 初めての利用の場合は画面の指示に従ってアプリを追加します。
    • すでにアプリを追加している場合は設定アイコンを選びます。
    • 「Manage Apps」で、「Add App」を選択します。
    • 指示に従います。
  • 3 : Mobile Analyticsを自分のアプリに結合します。

    • Mobile Analyticsを結合するには、アプリに適したプラットフォームのSDKを使用します。(以下のプラットフォームのSDKがあります)
      • Android app
      • iOS app
      • JavaScript app(*僕が今回使ったのはこれなのでこれだけリンクがはってあります。)
      • Unity app
      • Xamarin app
  • 4 : Amazon Mobile Analyticsレポートのデータを確認する


aws-sdk-mobile-analytics(npm)

https://www.npmjs.com/package/aws-sdk-mobile-analytics

イントロダクション

JavaScript用Mobile Analytics SDKを使用すると、JavsScript対応アプリケーションは、AWSコンソールでの分析、S3およびRedshiftへの自動エクスポートによって、イベントを作成して送信することができます。ライブラリはブラウザのローカルキャッシュAPIを使用してデータのローカルキャッシュを作成し、アプリケーションがオフラインのときでもWebアプリケーションでイベントをバッチおよび記録できます。

Setup

  <script src="/js/aws-sdk.min.js"></script>
  <script src="/js/aws-sdk-mobile-analytics.min.js"></script>

使い方

  • 1 : Amazon Mobile Analytics管理コンソールにログインし、新しいアプリを作成する。App IdとCognito Identity Pool Idに注意する。
  • 2 : Cognito Identity Pool IDを使用して、クレデンシャルプロバイダを初期化します。 これは、AWS SDKAmazon Mobile Analytics REST APIへの認証を管理するために必要です。
AWS.config.region = 'us-east-1';
AWS.config.credentials = new AWS.CognitoIdentityCredentials({
    IdentityPoolId: COGNITO_IDENTITY_POOL_ID   //Required e.g. 'us-east-1:12345678-c1ab-4122-913b-22d16971337b'
});
  • 3 : 上記の手順1で生成したアプリIDを含め、MobileAnalyticsManagerをインスタンス化します。セッションイベントは自動的に記録され、クライアントは10秒ごとにAmazon Mobile Analyticsにイベントを送信します。
var options = {
    appId : MOBILE_ANALYTICS_APP_ID   //Required e.g. 'c5d69c75a92646b8953126437d92c007'
};
mobileAnalyticsClient = new AMA.Manager(options);

手動でイベントの送信を強制するには次の呼び出しを行います。

mobileAnalyticsClient.submitEvents();

追加のオプション

カスタムイベント

オプションとして、カスタムイベントを追加して、追加情報をキャプチャすることができます。

mobileAnalyticsClient.recordEvent('CUSTOM EVENT NAME', {
        'ATTRIBUTE_1_NAME': 'ATTRIBUTE_1_VALUE',
        'ATTRIBUTE_2_NAME': 'ATTRIBUTE_2_VALUE'
        /* ... */
    }, {
        'METRIC_1_NAME': 1,
        'METRIC_2_NAME': 99.3
        /* ... */
    });

セッションの設定

デフォルトの設定ではセッションは10分間続きますが、optionsオブジェクトにsessionLengthを含めることで、Mobile Analytics Managerを初期化するときにこのデフォルト設定を上書きすることができる。

var options = {
    appId : MOBILE_ANALYTICS_APP_ID, 
    sessionLength: 300000   //ミリ単位でのセッションの長さです。この場合は、5分になります。
};
mobileAnalyticsClient = new AMA.Manager(options);

セッションのタイムアウトは、セッションの継続を可能にするように更新できる。

//以下の記述で、現在のセッションが5秒語に期限切れになります。
mobileAnalyticsClient.resetSessionTimeout(5000); 
    
//以下の記述で、初期化中に指定された時間を使用して、現在のセッションの有効期限がリセットされます。 
//デフォルトの設定をそのまま使用するとセッションは10分後に期限切れになります。 
mobileAnalyticsClient.resetSessionTimeout();

アプリの詳細をイベントに追加する

SDKを初期化するときに、追加のアプリケーションの環境と詳細をoptionsオブジェクトに追加するとこができます。これらの詳細はキャプチャされ、すべてのイベントに適用され、自動エクスポートを使用してデータのカスタム分析を行う場合に使えます。

 var options = {
    appId : MOBILE_ANALYTICS_APP_ID,       //Required e.g. 'c5d69c75a92646b8953126437d92c007'
    appTitle : APP_TITLE,                  //Optional e.g. 'Example App'
    appVersionName : APP_VERSION_NAME,     //Optional e.g. '1.4.1'
    appVersionCode : APP_VERSION_CODE,     //Optional e.g. '42'
    appPackageName : APP_PACKAGE_NAME,     //Optional e.g. 'com.amazon.example'
    make : DEVICE_MAKE,                    //Optional e.g. 'Amazon'
    model : DEVICE_MODEL,                  //Optional e.g. 'KFTT'
    platform : DEVICE_PLATFORM,            //Optional valid values: 'Android', 'iPhoneOS', 'WindowsPhone', 'Blackberry', 'Windows', 'MacOS', 'Linux'
    platformVersion : DEVICE_PLATFORM_VER  //Optional e.g. '4.4'
};
mobileAnalyticsClient = new AMA.Manager(options);

バイスの詳細が特に指定されていない時は、AmazonMobileAnalyticsがUser-Agentヘッダー値に基いてこれらの値を予測し指定します。これらの値は、使用できる場合初期化中に指定したほうがよいです。

詳しいドキュメント

詳しいドキュメントと追加の設定は以下のリンクから参照してください。 https://aws.github.io/aws-sdk-mobile-analytics-js/doc/AMA.Manager.html

ネットワーク構成

Amazon Mobile Analytics JavaScript SDKは以下のエンドポイントにリクエストを送信します。 * イベントを使う場合 : https://mobileanalytics.us-east-1.amazonaws.com * Cognito認証の場合: https://cognito-identity.us-east-1.amazonaws.com - このエンドポイントは、アイデンティティが作成された領域に基いて、変更されることがあります。 * ほとんどのフレームワークは、*.amazonaws.comですべてのAWSエンドポイントをホワイトリストに登録することで両方のエンドポイントをホワイトリストに登録することができます。

ブログをはじめました。

@tako_programingです。題の通りブログをはじめました。理由としては、普段から趣味やバイトでプログラミングをしていて、試行錯誤しながら色々やってるわけですが、その結果というか過程を自分のノートに書いて置いておくだけでは少しもったいないなぁと思いまして。対してそれに費やす労力も変わらないと思うのでどうせだったら公開していこうと思ったわけでございます。完全不定期でてきとうにやっていきますが、よろしくお願いします。