Contract使ってみた

以下、ネガティブな記事です。

public void compute(List<List<double>> A, List<double> b) {
  Contract.Requires(A != null);
  Contract.Requires(b != null);
  Contract.Requires(A.Count >= 3);
  Contract.Requires(A[0] != null);
  Contract.Requires(A[0].Count == A.Count);
  Contract.Requires(A.Count == b.Count);
  Contract.Requires((new Func<bool>(() =>
      Enumerable.Range(0, A.Count).SelectMany(
          i => Enumerable.Range(0, A[0].Count),
          (i, j) => Math.Abs(i - j) <= 1
              ? true
              : A[i][j] < 1e-10 /* A[i][j] == 0 */
          ).All(x => x)
      ))());
compute(
    new List<List<double>> {
        new List<double> { 1, 2, 0 },
        new List<double> { 1, 2, 3 },
        new List<double> { 0, 2, 3 },
        new List<double> { 4, 2, 3 } },
    new List<double> { 4, 5, 6, 7 });


上のコードをビルドすると、チェックが走って以下のログが出力ウィンドウに表示される。
unprovenというのはチェックできないという意味らしい。
localtionの行をダブルクリックすると違反している契約にジャンプできる。

<path>\src\Main\Program.cs(17,17): warning : CodeContracts: requires unproven: A[0] != null
<path>\src\TridiagonalSolver\GaussianElimination.cs(21,13): warning :   + location related to previous warning
<path>\src\Main\Program.cs(27,17): warning : CodeContracts: Possibly calling a method on a null reference 'v'
<path>\src\Main\Program.cs(17,17): warning : CodeContracts: requires unproven: A[0].Count == A.Count
<path>\src\TridiagonalSolver\GaussianElimination.cs(22,13): warning :   + location related to previous warning
CodeContracts: Main: OutputError,requires unproven: (new Func<bool>(() =>
CodeContracts: Main: \n                Enumerable.Range(0\, A.Count).SelectMany(
CodeContracts: Main: \n                    i => Enumerable.Range(0\, A[0].Count)\,
CodeContracts: Main: \n                    (i\, j) => Math.Abs(i - j) <= 1
CodeContracts: Main: \n                        ? true
CodeContracts: Main: \n                        : A[i][j] < 1e-10 /* A[i][j] == 0 */
CodeContracts: Main: \n                    ).All(x => x)
CodeContracts: Main: \n                ))(),,,top,True,False,warning,<path>\src\\Main\\Program.cs,17,17,23,81
<path>\src\TridiagonalSolver\GaussianElimination.cs(24,13): warning :   + location related to previous warning

Contractでチェックできるのは引数などの呼び出し側からアクセス可能な変数。
privateなメンバ変数をチェックすることはできない。(忘れたけどwarning表示だったかな。)
あとはPureAttributeになっているものをContract内に書けるらしい。


A[0]やA[0].Countがunprovenになっている。
インデクサがPureになっていないという理由か?
検証できないと言っているっぽい。
とはいえ、Listのインデクサは触れないコードだ。


途中で出ている長ったらしい出力は二次元のListが三重対角行列であるかをチェックしているコードなのだが、
この中で使っているメソッドも全滅っぽい。
# 表示がなんか変ですね。


.NET Frameworkのライブラリが完全にサポートされていない状況を見るに、Contractでなんでもチェックしようと考えてはダメみたいですね。
Contractを埋め込もうとしているアセンブリが、対応していないアセンブリに依存していると、
unprovenになる状況が生まれやすくなると思います。
引数のnullチェック程度の比較的簡単なものに留めるのがいいのかもしれないと思いました。


使いどころが難しそうです。


VS2015Communityで確認しました。
Contractを使うためにはCode Contracts for .NETが必要です。

[プログラミング]SonarQubeでC#のコードを解析する(Windows).2 - OpenCover

前回(d:id:pneumaster:20151229:1451408205)の続きです。

確認した環境

前回に追加して以下の環境を持ちます。

https://github.com/opencover/opencover/releases
Releaseバージョンを選びます。
# どこで拾ってきたのか4.5.2506を使って無駄な時間を過ごしました。

  • ReportGenerator 2.3.5

https://github.com/danielpalme/ReportGenerator/releases

SonarScannerのセットアップ

Getting Startedを前回の続きから進めると次はSonarQube Scannerをダウンロードすることになります。

今回はVisual Studioを使うので、リンク先のScannerではなくSonarQube Scanner for MSBuildを使います。
# 私が確認した時のバージョンは1.0.2(19 Oct. 2015)でした。(MSBuild.SonarQube.Runner-1.0.2.zip)
# このページの動作リストに2015 Communityが書かれていませんが、解析できました。

SonarQube本体と同様に適当にzipを伸長、配置します。


SonarQubeの設定

C#プラグインをセットアップします。

MSBuild.SonarQube.RunnerのPrerequisitesとしてC#プラグインが必要です。
起動しているSonarQubeのWebサーバにログインします。(前回最後に書きました。)


ページ上部のAdministrationからSystem->Update Centerに進みます。
Availableを押すとインストール可能なプラグインが出てくるのでC#の右側にある[Install]を押します。
Install Pendingが表示されたら一旦SonarQubeを再起動します。
前回の通りにStartSonar.batを起動している場合には、コマンドプロンプト(... is upが表示されているはず)でCtrl+Cを押すと終了させることができました。(終了フェーズが動くので「バッチを終了しますか?」が出るまで待ちます。そうしたらyで終了できます。)

同じように起動してログインするとAdministrationのページの左部CATEGORYにC#が表示されました。

次に解析対象のプロジェクトをSonarQubeに作成します。

Administration->Projects->Managementで表示されるページの右のCreate Projectボタンを押します。

  • Name:tempProject
  • Key:tmppj

としました。Branchは必須ではなさそうなのでここでは無記入としました。
追加されたtmpProjectプロジェクトを押すとDashboardが表示されました。
No analysis has been performed since creation.
まだソースファイルをアップロードしていないので解析結果がありません。

解析対象のソースコードを用意します。

今回はhttps://github.com/pneu/ForSonarQubeを使うことにします。
上でSonarQubeのプロジェクトを作った時のNameとKeyを使って次のコマンドを実行します。
kがkey、nがNameです。v(version)は指定しないと引数が足りないと怒られるので適当な値を与えました。
コマンドはVisual Studioについてくる開発者用コマンドプロンプト上です。

>MSBuild.SonarQube.Runner begin /k:tmppj /n:tmpProject /v:0.1
>msbuild
>MSBuild.SonarQube.Runner end


完了後下記のURLが表示されます。

18:17:12.789  Analysis results: http://localhost:9000/dashboard/index/tmppj
Post-processing succeeded.

アクセスするといろいろ解析結果が表示されていました。
Issuesページには全体の問題点が表示されています。
あとは修正後コミットを繰り返していくことになります。


以上が一連の解析プロセスになります。
プラグインC#以外にも色々用意されています。

カバレッジ

OpenCoverを使いました。


上述した一連の流れにOpenCoverのコマンドを挟みます。
MSTest以外を使う場合は適宜変えてください。
ユニットテストのプロジェクトは上に書いたGitHubのコードを使っています。

>MSBuild.SonarQube.Runner begin /k:tmppj /n:tmpProject /v:0.1
>msbuild
>opencover.4.6.166\OpenCover.Console -output:opencover.xml -register:user -target:"%VSINSTALLDIR%\Common7\IDE\MSTest.exe" -targetargs:"/nologo /testcontainer:UnitTestProject1\bin\Debug\UnitTestProject1.dll"
>MSBuild.SonarQube.Runner end


それからSonarQubeのDashboardを見てみると、ページに[Unit Test]の項目が追加されていました。

パーセントをクリックするとコードとの対応も見ることができます。


続いて、ReportGeneratorでレポートを生成します。
ここではopencoverが生成したテスト結果からhtmlファイルを生成しますが、それ以外についてはhttps://github.com/danielpalme/ReportGenerator#usageを見てください。生成されるレポートのイメージも載っています。
texでも出力できるみたいなので体裁にこだわる場合はpdfも生成できるわけですね。


さて、htmlを出力するコマンドは以下の1つです。
htmlフォルダ以下にいくつかのファイルが出力されます。

>ReportGenerator_2.3.5.0/bin/ReportGenerator.exe opencover.xml html


以上、めでたく完了です。

[プログラミング]SonarQubeでC#のコードを解析する(Windows)

SonarQubeをWindows環境で使ってC#コードを解析するに書かれている内容の多くを参考にさせていただきました。
記事の日時が2013年なので今の状況と異なる点をセットアップしながらまとめました。

はじめに注意
* ココらへんの事柄は一年も経てば色々変わったりするので、古い情報はあんまり真に受けないこと。

私も書いておこう。

確認した環境

  • Win8.1 / 64bit
    • 主に一般ユーザ権限
  • SonarQube 5.2
  • Java 1.8
    • 本記事とは無関係の諸事によりjdkを使いました。
  • MySQL Community 5.7.10

まず必要なファイル

  • SonarQube

http://www.sonarqube.org/
Downloadから。


その他の環境は、SonarQubeのRequirementsを参考にしてください。
Java/DB/Web Browserについて書かれています。
以下は私がセットアップした時の環境です。DBMSは個人的な趣味*1によりMySQLにしました。

32/64bitの別は関係ないようですが、SonarQubeは32bit版のJVM上で動いているっぽいです。

Windowsではhttp://dev.mysql.com/downloads/installer/MySQL Installというオールインパッケージがありました。
Linux向けにもレポジトリが用意されているのが確認できます。

Getting Started

MySQLのセットアップ

SonarQubeが参照するデータは、このDBに蓄積されます。


お行儀よく、MySQLSonar用のユーザを作りました。
WorkbenchをインストールしてあればSidebarのUsers and PrivilegesからAdd Accountすることができました。

  • Login Name:sonar
  • Authentication Type:Standard
  • Administrative Roles:UserAdmin

にしました。

# Sidebarはデフォルトで左側に表示されていると思います。
# 表示されていなければViewメニューから表示できるようです。


それからsonarユーザが操作できるデータベースを管理者権限で作ってから、sonarユーザにはそのデータベースだけを操作できるように制限しておきました。
データベースの作成はツールバーのCreate a new schema ...から作成することができます。
SQLでやるならcreate databaseですね。
# create schemaはcreate databaseのシノニムだそうです。
# https://dev.mysql.com/doc/refman/5.6/ja/create-database.html
権限は上で書いたUsers...画面から設定することができます。ユーザ作成時にやるのがいいですね。
作った後でも変更可能です。
また、このデータベース内の全権限を与えておきました。

SonarQubeのセットアップ

http://docs.sonarqube.org/display/SONAR/Documentation/
のGetting Startedを順に進めていきます。
Requirementsは読んだと思うので、Installing SonarQubeを進めます。

上でダウンロードしたSonarQubeを適当なフォルダに伸長配置します。


conf/sonar.propertiesを開いて以下を編集します。

ユーザ名とパスワードはMySQLで作ったユーザ。

MySQLならコメントアウトするだけで使えるようになっています。
ただし、データベース名はsonarがデフォルトになっています。
違う名前で作った場合は、localhost:3306/の後ろのsonarを変更します。
MySQL以外のDBもテンプレがあります。


起動してみます。
bin\windows-x86-64かbin\windows-x86-32のどちらかのStartSonar.batを起動します。
Firewallを開くかをWindowsから聞かれますが開きます。

jvm 1    | 2015.12.30 01:18:33 INFO  app[o.s.p.m.Monitor] Process[web] is up

が表示されたら正常に起動完了しました。


もし下記のように途中で失敗して終了してしまった場合には、

2015.12.27 00:06:20 INFO   es[o.s.p.StopWatcher]  Stopping process
2015.12.27 00:06:20 INFO   es[o.elasticsearch.node]  [sonar-1451142364783] stopping ...
2015.12.27 00:06:20 INFO   es[o.elasticsearch.node]  [sonar-1451142364783] stopped
2015.12.27 00:06:20 INFO   es[o.elasticsearch.node]  [sonar-1451142364783] closing ...
2015.12.27 00:06:20 INFO   es[o.elasticsearch.node]  [sonar-1451142364783] closed
2015.12.27 00:06:20 INFO  app[o.s.p.m.TerminatorThread] Process[search] is stopping
2015.12.27 00:06:20 INFO  app[o.s.p.m.TerminatorThread] Process[search] is stopped

logs\sonar.logを見てみます。
私はFirewallを開いていなかったりMySQLの設定をしていなかったりしたので、失敗してスタックトレースが残されていました。


ところで私の環境では64bitのSonarQubeを起動すると以下の警告が表示されました。

jvm 1    | WARNING - Unable to load the Wrapper's native library 'wrapper.dll'.
jvm 1    |           The file is located on the path at the following location but
jvm 1    |           could not be loaded:
jvm 1    |             C:\<snip>\sonarqube-5.2\bin\windows-x86-64\.\lib\wrapper.dll
jvm 1    |           Please verify that the file is readable by the current user
jvm 1    |           and that the file has not been corrupted in any way.
jvm 1    |           One common cause of this problem is running a 32-bit version
jvm 1    |           of the Wrapper with a 64-bit version of Java, or vica versa.
jvm 1    |           This is a 32-bit JVM.
jvm 1    |           Reported cause:
jvm 1    |             C:\<snip>\sonarqube-5.2\bin\windows-x86-64\lib\wrapper.dll: Can't load AMD 64-bit .dll on a IA 32-bit platform
jvm 1    |           System signals will not be handled correctly.

wrapper.dllが32bitなので64bitモデルのJVM上だと動かないよってことなんでしょうか。
でも結局64bitのJavaをインストールしていれば2つのフォルダのどちらでも動いたので、32bitにフォールバックしているんですかね。(32の方を起動すれば上の警告は表示されませんでした。)

SonarQubeが起動した後は...

さて、SonarQubeはWebサーバを同梱しています。起動した後はhttp://localhost:9000/にアクセスすれば、ウェルカムページを見ることができます。

右上のLog inからadmin/adminでログインできることを確認しておきます。
# 設定した記憶がありませんが、デフォルトなんでしょうね。


長くなったので、コード解析をするのは次回にします。
(追記)⇒書きました。(d:id:pneumaster:20151230:1451480090)

*1:http://db-engines.com/en/rankingで上位だったので。。。

haskell:レコード構文

代入しているように見える。

レコード構文のシンタックス

定義
data Person = Person { firstName :: String  
                     , lastName :: String  
                     , age :: Int  
                     , height :: Float  
                     , phoneNumber :: String  
                     , flavor :: String  
                     } deriving (Show)  
構築

1つ目

let guy = Person "Buddy" "Finklestein" 43 184.2 "526-2928" "Chocolate"


2つ目

let guy = Person { firstName = "Buddy"
                 , lastName = "Finklestein"
                 , age = 43
                 , height = 184.2
                 , phoneNumber = "526-2928"
                 , flavor = "Chocolate"
                 }


2つ目の書式でPersonを構築するときは、すべてのフィールドを指定しなくてもいい。(ただしフィールドが「インスタンス化されていない」という警告が出る)

main :: IO ()
main = do
  let guy = Person { firstName = "Buddy"
                 , lastName = "Finklestein"
                 , age = 43
                 -- , height = 184.2
                 , phoneNumber = "526-2928"
                 , flavor = "Chocolate"
                 }
  print guy
> :l main.hs
[1 of 1] Compiling Main             ( main.hs, interpreted )

main.hs:11:13: Warning:
    Fields of `Person' not initialised: height
    In the expression:
      Person
        {firstName = "Buddy", lastName = "Finklestein", age = 43,
         phoneNumber = "526-2928", flavor = "Chocolate"}
    In an equation for `guy':
        guy
          = Person
              {firstName = "Buddy", lastName = "Finklestein", age = 43,
               phoneNumber = "526-2928", flavor = "Chocolate"}
    In the expression:
      do { let guy = ...;
           print guy }
Ok, modules loaded: Main.


遅延評価のため、インスタンス化されていないフィールドを使ったタイミングで例外を送出する。

Person {firstName = "Buddy", lastName = "Finklestein", age = 43, height = *** Ex
ception: main.hs:(11,13)-(17,18): Missing field in record construction Main.height

構築2

ここで構築済みのPerson値に、2つ目の書式でフィールドの調整をすることができる。

main :: IO ()
main = do
  let guy = Person { firstName = "Buddy"
                 , lastName = "Finklestein"
                 , age = 43
                 -- , height = 184.2
                 , phoneNumber = "526-2928"
                 , flavor = "Chocolate"
                 }
  print guy {height = 184.2}
> main
Person {firstName = "Buddy", lastName = "Finklestein", age = 43, height = 184.2,
 phoneNumber = "526-2928", flavor = "Chocolate"}

応用例

状態保持。
XXX


処理分割。

data Foo { turn :: XXX
         , time :: XXX
         , a :: XXX
         }

foo f = final f where
  mae_shori = Foo (turn f + 1) (time f % 2)
  final = mae_shori {a = xxx} -- aだけ更新

ABC008:D 高速累乗アルゴリズム

累乗を高速に計算する問題に感動したのでメモ。

問題:http://abc009.contest.atcoder.jp/tasks/abc009_4
解説:http://www.slideshare.net/chokudai/abc009 (pp.56-106)


(1)ナイーブな解き方

ghci> main
5 100
2345678901 1001001001 3333333333 3141592653 1234567890
2147483648 2147483647 4294967295 4294967294 3434343434
1067078691
(0.03 secs, 1519860 bytes)

ghci> main
30 999999999
11627 5078 8394 6412 10346 3086 3933 668 9879 11739 4501 6108 12336 8771 2768 2438 2153 7047 5476 313 1264 369 12070 10743 10663 747 370 4671 5235 3439
114 3613 3271 5032 11241 6961 3628 150 12191 2396 7638 3046 11594 8162 11136 786 9878 2356 11660 1070 3649 10882 9746 1415 3307 7077 9319 9981 3437 544
<interactive>: out of memory

(2) 高速累乗

ghci> main
5 100
2345678901 1001001001 3333333333 3141592653 1234567890
2147483648 2147483647 4294967295 4294967294 3434343434
1067078691
(0.00 secs, 1067548 bytes)

ghci> main
30 999999999
11627 5078 8394 6412 10346 3086 3933 668 9879 11739 4501 6108 12336 8771 2768 2438 2153 7047 5476 313 1264 369 12070 10743 10663 747 370 4671 5235 3439
114 3613 3271 5032 11241 6961 3628 150 12191 2396 7638 3046 11594 8162 11136 786 9878 2356 11660 1070 3649 10882 9746 1415 3307 7077 9319 9981 3437 544
2148
(0.30 secs, 158523952 bytes)


問題に付いている入力例3の計算量の違いを見てみると、
(1)ナイーブ
O(n^3 * m)

ghci> 30^3 * (999999999 - 30)
26999999163000

2.7e+13


(2)高速
O(n^3 * lb m)

ghci> 30^3 * (logBase 2 $ 999999999 - 30)
807228.5258500932

8.1e+5


計算量は解説105ページに書かれています。
とんでもない違いが出るのですね。実感しました。


#背景が黒だとtex記法が見えないのかな?

社内の共有フォルダをgit管理する方法(git separate-git-dir)

遅まきながらgit initやcloneにあるseparate-git-dirオプションを使いました。
ちょろっとググると、Dropboxでの応用があるみたいですね。
このエントリではトリッキーかもしれませんが、git initでの違う応用を紹介します。

separate-git-dirを使うと何ができるの?

チームで使っているWindowsの共有フォルダをローカルで版管理しています。
これは、個人的なバックアップと差分チェックを兼ねたものです。


この共有フォルダは、
諸事情によりPJに関係ない人でもアクセスできるフォルダなので、
gitレポジトリ(.gitディレクトリ)を作りたくありません。
PJに関係ないファイルということと、レポジトリを壊されたくないからです。
共有フォルダの中身を、rsyncでローカルへ同期してから版管理していました。


separate-git-dirを使うことで、
共有フォルダとは別の場所へレポジトリを作ることができますので、
ローカルにレポジトリを作ることで、当初の目的を果たせます。


最初に注意です。
本エントリは共有フォルダを直接、作業フォルダにする方法を書いています。
これは言い換えると、gitのcheckoutやresetコマンドで、
共有ファイル上の変更を虚空の闇に葬り去ることが可能であることを言っています。
くれぐれもメンバに恨まれないように。。。

共有フォルダをgit管理する方法

$ git --version
git version 1.8.1.msysgit.1
$


worktreeを別のホスト上にある共有フォルダとします。
# zドライブへ割り当てているとします。

$ mkdir -p /z/path/to/worktree
$ cd /z/path/to/worktree/
$ git init --separate-git-dir=/c/route/to/repo
Initialized empty Git repository in c:/route/to/repo/
$

これで共有フォルダworktreeとは別に、
ローカルPCにあるrepoへレポジトリを作成できました。


worktree、repoの双方でレポジトリに対するアクセスが可能です。

$ pwd
/z/path/to/worktree
$ touch foo
$ git add foo
$ git status --short
A  foo
$ cd /c/route/to/repo/
$ git status --short
A  foo
$


initしたタイミングで、worktreeに「.git」というファイルが作られます。
.gitファイルにはレポジトリのパスが書かれています。
worktree側でもレポジトリにアクセスできるのは、この内容を伝っているのでしょう。

$ ls -a /z/path/to/worktree/
./  ../  .git  foo
$ cat /z/path/to/worktree/.git
gitdir: /c/route/to/repo
$

つまりworktreeにある.gitファイルは、
共有フォルダ→ローカルホストのレポジトリへのアクセスに必要です。

共有フォルダにPJとは関係のないファイルを置きたくない

ここまでの手順では、共有フォルダ側に.gitファイルが置かれていることになります。
.gitディレクトリがファイルになっただけです。
レポジトリの破壊は免れるでしょうが、PJとは関係の無いファイルがあることに変わりはありません。
どうしましょう。


結論から言うと、共有フォルダにある.gitファイルは消してしまっても版管理はできました。
ただし共有フォルダ側からレポジトリアクセスはできなくなります。

$ pwd
/z/path/to/worktree/
$ rm .git
$ git status
fatal: Not a git repository (or any of the parent directories): .git
$


この状態では、レポジトリ側から一方的に共有フォルダの変更が見えます。

$ pwd
/z/path/to/worktree/
$ touch bar
$ ls -a
./  ../  bar  foo
$ cd /c/route/to/repo/
$ git status --short
A  foo
?? bar
$


版管理もできます。

$ pwd
/c/route/to/repo
$ git commit --quiet -m 'initial'
$ git add bar
$ git commit --quiet -m 'modified'
$ git log --abbrev-commit --oneline master
5a6c4df modified
a96ad94 initial
$ ls -a /z/path/to/worktree/
./  ../  bar  foo
$ git checkout --quiet HEAD^
$ git rev-parse HEAD
a96ad94174f4559ab4bfffddd49ba184c9b9324a
$ ls -a /z/path/to/worktree/
./  ../  foo
$

ただしコマンドラインで作業していて困る点は、
レポジトリ側で補完が効かないことです。
これはまあ仕方ないですね。

.gitファイルを消したのにレポジトリ側から変更が見える理由

これはレポジトリのconfigファイルにcore.worktreeというオプションが設定されているためです。

$ git config --local --get core.worktree
/z/path/to/worktree
$

これが、ローカルホスト→共有フォルダのレポジトリへのアクセスに必要となるようです。

x1c来てからやること備忘録




  • のどかのソース⇒クラス図