読者です 読者をやめる 読者になる 読者になる

出羽と発火後忘失

FIRE AND FORGET

SQLのランダム関数がランダムすぎる、あるいはランダムでない

ネタ元はこちら:

create table my_table (my_number integer);
insert into my_table values (0), (1);

としたテーブルに対して

select * from my_table where id = <ランダム関数を用いて求めた整数値を2で割った余り>;
// 例: MySQLの場合
select * from my_table where id = (floor(rand() * 100) % 2);

このようなSQLを実行すると、どのRDBMSの場合でも、結果は必ずどちらか1レコードのみが得られる…わけではない、と。
0行だったり2行だったりするRDBMSもあります。

理由はMySQLのリファレンスに書いてあったのですが、

WHERE 句内の RAND() は、WHERE が実行されるたびに再評価されます。

ということで、1レコード目の評価では0だったとして、2レコード目の評価時にも0である保証はない、ということですね。

一方で、SQL Serverではそうではなく、上記のような状況で常に同じ値を返しますので、どちらの評価時にも0、あるいはどちらの評価時にも1の2パターンになります。

気になったので、ランダム関数がどちらのRDBMS方式なのか、メジャーなものを対象に調べてみました。

結果として、SQL Serverだけ特殊、ということになりました。
そういえばASEはどうなんだろう…と検索してみたところ(仮想環境的なものは見つかりませんでした)、オンラインマニュアルに次のような記述がありました。

Unlike rand, rand2 is computed for each returned row when it is used in the select list.

というわけで、SQL Serverと同じ~rand~以外に、~rand2~という関数も備えているようです。
(ただ、used in the select list という意味がわかってません。もしかしたらwhere句では使えないってことなのかも…?)

Androidアプリ開発でMySQL Connector/Jを追加してビルドしようとするとエラーになる

ネタ元:

AndroidJDBCドライバを直接使うっていうのが「その発想は無かったわ!」てな感じでかなり衝撃を受けたのですが、ググってみるとそれなりに事例はありそうです。
まあ、いわゆるDBアプリが今全部Webアプリに置き換わったわけでもないですし、Androidをクライアントに使うとなるとさもありなん、てな感じなんでしょう。

本題です。
Androidアプリビルド時に mysql-connector-java-5.1.37-bin.jar を依存関係に入れていると、次のようなエラーメッセージが出力されます。

Caused by: com.android.dx.cf.iface.ParseException: bad class file magic (cafebabe) or version (0034.0000)

クラスファイル解析しようとしたけどファイル形式がおかしいので失敗した、ということなんですが、大抵の場合はAndroidが対応していないバージョンのライブラリを追加した場合にこのエラーになります。

で、その大抵の場合、見るべきポイントは version (0034.0000) の部分で、今回は数値が 34 となっています。
WikipediaのJavaクラスファイル のページ内をこの 34 で検索すると、Java8向けのファイルであることがわかり、AndroidではJava8未対応なので無理なんだな、と納得できます。

なぜ5.1.37(以降)はJava8向けなんだろう、という点については、おそらくJDBC4.2対応のためでしょう:

It is also the first release of MySQL Connector/J to support the Java Database Connectivity (JDBC) 4.2 API.

MySQL :: MySQL Connector/J Release Notes :: Changes in MySQL Connector/J 5.1.37 (2015-10-15)

補足すると、おそらく通常のJava上で使用する分にはJava8は要求されません。なぜなら、Java8未満で使用する分には、JDBC4.2関連のクラスをロードする必要がないためです。JDBC4.2関連クラス以外はJava5向けにビルドされているようです。

javap コマンドを.classファイルに対して実行すればバージョンがわかりますので試してみましょう:

$ javap -v VersionFSHierarchyMaker.class |grep "major version"
  major version: 49

(Androidコンパイラは16進表記でしたが、こちらは10進表記ですね)

Androidコンパイラがエラーを出すのは、実際に使用している/していないにかかわらず、jarの中の.classを全て解析しているからです。
コンパイル時にはまだ実際に必要かどうかは判断できないですからね。

Eclipse4.5でimportの編成を行うと並び順が変わる

参考リンク

元ネタはこちら:

関連するバグレポートは、(上記回答にも書きましたが)こちら:

今回使用したソースはこちら(Mainクラスでimportソート順が見られます):

事象

Eclipse(4.4/4.5とも)のimportの並び順は Organize Imports で設定できるのですが、デフォルトでは以下のようになっています。

f:id:yukihaned:20151220164039p:plain

この設定のまま 4.5 で Source > Organize Imports (Ctrl+Shift+O; 日本語ではimportの編成、でしょうか)を行うと次のような並び順になります。

import java.net.HttpURLConnection;
import java.util.ArrayList;

import javax.swing.JFrame;

import org.w3c.dom.DOMException;

import com.ClassCom;

import a.ClassA;
import javaj.ClassJavaj;
import jp.mycompany.myproject.ClassJp;

日本語で説明するとこんな感じでしょうか。

  • 設定で順序が明示されているものはその順に並ぶ。
  • 設定で順序が明示されていないものは、明示されたパッケージの後ろに、アルファベット順で並ぶ。

次に 4.4 の場合です。

import java.net.HttpURLConnection;
import java.util.ArrayList;

import javaj.ClassJavaj;

import javax.swing.JFrame;

import jp.mycompany.myproject.ClassJp;

import org.w3c.dom.DOMException;

import a.ClassA;

import com.ClassCom;

こうして比較して見てみると、随分違いますね。
前述bug reportには "best match" という用語が何度か登場していて、特定のアルゴリズムを指す名称のような雰囲気があるのですが、具体的にどういうアルゴリズムなのかは検索しても分かりませんでした。
そこで実際に試してみたところ、並び替えた結果は次のようになっているようでした。

  • 設定で順序が明示されているものはその順に並ぶ。
  • 設定で順序が明示されていないものは、アルファベット順に並べたとき、直後にくる明示されたものの設定に従う。

今回の場合ですと、それぞれ、 javajjavaxjporgacom の設定に従っているわけですね。

対策

日本で使っていてこの問題に遭遇する頻度は jp パッケージが最も高いと思うのでこれを例に挙げると、 Java > Code Style > Organize Imports の設定で、 javaxorg の間に jp を追加しておけば、4.4の並び順が4.5でも再現できることになります。

アドバンス

そもそもEclipseのデフォルト設定がなぜこの順になっているかを考えると、

  • 標準的なパッケージほど先頭に置く
  • (逆に言うと)自作パッケージは最下部

という想定なのだと思われます。
ここで自作パッケージは com (とか org )しか考慮されていないのが非アメリカ人にとって問題だったのでしょう。
日本人としては、Organize Importsの設定で、最下部に jp が入っていれば問題が起きにくかったと言えます。

4.5の挙動はその意味では妥当だと思います。が、以前のコードに変更が入るとなると、すんなりとは受け入れられない場面もあることは容易に想像できますね…

teratailってどんななの?

teratail で週間1位を取りました!

f:id:yukihaned:20151220114358p:plain

…というわけでアカウント作って多分2週間くらいなんですが、少し感想を書いてみたいと思います。
ちなみに私が同類で他に利用しているサービスとして Stack Overflow日本語版(SO) がありまして、以下、念頭にSOと比較して…なんてのもあります。

簡潔に言うと

  • 宿題を丸投げしているような質問も比較的受け入れてくれる
  • 回答のスピードは早いことが多い、ただし、的を射た回答か/妥当性はあるのかというのは怪しいことも少なくない
  • 現時点では「プログラマ限定」的な広告の謳い文句によって、ある程度以上のリテラシは保たれているように思う

もうすこし詳しく、私が感じたteratailの特徴(主に回答者として)

  • 回答すれば得点を得られる*1
    • 本来「情報の追加・修正依頼」として書かれるべき内容が、回答として書かれがち(前者だと得点が得られない)。(「情報の追加・修正依頼」欄が目立たなさすぎる問題もありますが)
  • Wikipediaで言うところの「要出典」「独自研究」的な回答が少なくない
    • 質より量回答した方が得点を得やすいため。
  • 投票機能があまり適切に機能していない
    • これも回答するに当たって質より量へインセンティブを働かせる効果になっている。適当なことを書いてマイナス1票入ってもトータルで±0とかなので。
  • 質問者が適切だと思う回答(ベストアンサー)に付与する得点が高い
    • 分からない(から質問しているわけで)人がベストかどうか判断できるのだろうか?という問題。
    • 投票機能が機能していないのでベストアンサーが最も適切な回答であると誤解を招く(本来の意味は質問者の問題これで解決した、というだけ)。

利用していて個人的に最も気になったのが上で書いたうち「要出典」な回答*2。ベストアンサーなんて付いてると特に。
ここでツッコんでもおそらく質問者はもう興味無いだろうし、仮に回答者とfightになったら自分/回答者/コミュニティ誰も得しないし…とか考えてしまい、結局そのまま放置することに。
SOより質問及び回答の有効範囲やライフサイクルは限定的なのだ、と割り切るがほとんどです。

その他、サービス自体について

  • RSSフィードが無いのは非常に不便
  • モバイルページとPC用ページを切り替えるとログイン状態が想定と異なることになっている
    • 詳しく見たわけではないのですが、スマートフォンで見るときモバイル表示だと操作しづらいのでPCに切り替えたりその逆をやってると、さっきログイン状態だったのにログアウトしてる、ってことがよくあります。
  • ログアウト状態で書き込みを行うとログイン処理後書き込もうとしたメッセージがなくなる
    • 上記の予期せずログアウト状態になってるのと組み合わせるとかなり凶悪です…

後発のサービス(検索してみたところ2014年7月頃スタートしたようです)ならでは!みたいなところは無く、逆にちょっと残念な感じかな、と。
前節に記載した得点配分についても、もうちょっと工夫の余地はあるんじゃないかと考えます。

まとめ

teratailのファーストインプレッションを忘れないうちに書きとどめました。
総じて、何も書かないより変なことでも書いた方がまし、というような設計(得点設計)になっており、それが冒頭で述べた特徴につながっているのだと思います*3
利用者の参加に対する心理的ハードルは下がる代わりに、運営者の難易度は上がる調整ですね。

*1:SOでは回答しただけでは得られない

*2:こちらの言い回しを借りると、本来十分に「確認できる情報」であったとしても

*3:SOでは変なこと書くくらいなら何も書かないほうがましな設計になっていますね

「マークダウンする」

今年のモヤモヤコンピュータ(?)用語第1位です。

コードはマークダウンして記述してください。

某所で見かけた文章ですが、これを見つけて以来頭から離れません…(で、blogにでも書き出せば区切りつけられるかな、と)。

何がモヤモヤの原因か考えたのですが、次のような感じでしょうか。

  • 実際にやってほしいことは「マークアップする」ことであって、「マークダウンする」だと全く逆のように見える
  • Markdownは固有名詞なわけで、例えばはてなblogの記法で他の用語に差し替えると「はてなする」と同類
  • けどまあMarkdown記法でマークアップして欲しいんだな、ということは伝わる

現状「ググる(Googleする)」が普通に意味通じてるわけで、「マークダウンする」に違和感を持つのは単純に生まれてからの期間の長短の問題なんですかね…

メンバーが全てSerializableでないとシリアライズ出来ない…わけではない

スタックオーバーフローサイトからひっぱてきたネタ です。

問題

下記のような、 Child 型(Serializable をimplementsしていない)をフィールドとして持つクラス Parentシリアライズできるでしょうか?

class Parent implements Serializable {

    private Child child;

    // ...
}

class Child {
}

答え

シリアライズしようとしている対象オブジェクトの状態によります。
常にシリアライズ不能というわけではありません。

最も単純な例で言うと、 child フィールドが null であればシリアライズ時に NotSerializableException はスローされません。
また別の例としては、 Child のサブクラスで Serializable な型のインスタンスがセットされている場合も例外はスローされません。

コードサンプル

実行可能サンプルコードをこちら に置いています。

日本語版スタックオーバーフローで徳を積めば本家StackOverFlowにもご利益がある

知りたいことを検索してみたらstackoverflow がヒットして、参考になったからいいねボタン押したい、みたいなことがよくあると思うんですよ。
でもupvoteしようとすると権限ないから駄目って言われる。

権限を得るためには信用度を上げる必要があって、ポイント(正確には信用度)を上げるためにはええと、英語で質問したり回答しないといけないのか…ううんハードル高いな…てな感じですよね。

本当に?

というわけでこちらのリンク。

確かにほぼ全て質問するか回答するかしないかしないとポイント貰えないのですが、注目すべきは最後のこれ。

  • サイトの関連付けボーナス: +100 (各サイトで1回ずつ)

こいつは質問も回答もしなくてもポイントがもらえる仕組みなんです。

何をすればいいかというと、その直後の文章にある通りです。

経験豊富な Stack Exchange ネットワーク ユーザーで、すくなくとも 1 つのサイトで信用度が 200 点以上ある場合、基本的な新規ユーザーの制限を通過できるように、最初に +100 点の信用度ボーナスが与えられます。

この「すくなくとも1つのサイト」に日本語版スタックオーバーフローも当然含まれているわけで、つまり日本語版スタックオーバーフローで200点ポイントを稼げばstackoverflow.com(等)でもupvoteできるようになるわけです!

どうでしょう、この一点だけでも日本語版スタックオーバーフローを使いたくなってきません?


stackoverflowについて書いたついでに、最近参照して記憶に残ってる関連エントリを参考としてくっつけておきます。