32ビット?64ビット?(自分用メモ)

■gccが32ビットか64ビットか:
gcc -v の出力の Target: のところを見る.

32ビットの場合 Target: mingw32
64ビットの場合 Target: x86_64-pc-msys

などのようになる.システムの細かな差異によって文字列は少しずつ違うが,64ビットの場合 x86_64 が部分文字列としてしばしば含まれる.

■ghcが32ビットか64ビットか:
次のようなファイルを作る:

main = print ()

そして

$ ghc --make foo.hs
[1 of 1] Compiling Main             ( foo.hs, foo.o )
Linking foo.exe ...

$ file foo.exe

32ビットの場合には
PE32 executable (console) Intel 80386, for MS Windows

64ビットの場合には 
PE32+ executable (console) x86-64, for MS Windows

のようになる.

■おまけ:
自分がどの gcc を使ってるのかわからなくなった場合などは
which -a gcc
すればよい.使い分けをしたい場合は適宜 .bashrc に alias を張ればよさそう.

広告

HaskellからFFIでCの配列を扱う(マーシャリング)

HaskellのFFIについてはこのブログでも例えばこんな記事で扱っている.

今回はその記事では触れなかったマーシャリングについて扱う.まずは次のようなCの関数があったとしよう:

#include <stdlib.h>
#include <stdio.h>
#include "c_header.h"

/*
int sum(int* p);

int fib(int** pp, int n);
*/

int sum(int* p, size_t n)
{
  if(n == 0){ return 0; }
  size_t i;
  int retval = 0;
  for(i = 0; i < n; ++i)
  {
    retval += p[i];
  }
  return retval;
}

int fib(int** pp, int n)
{
  if(n<=0){ return 1;}
  *pp = malloc(n * sizeof(int));
  if(n>=1){ (*pp)[0] = 1;}
  if(n>=2){ (*pp)[1] = 1;}
  if(n>=3){
    int i;
    for(i=2; i < n; ++i)
    {
      (*pp)[i] = (*pp)[i-1] + (*pp)[i-2];
    }
  }
  return 0;
}

これらのコードは次のように使われる:

#include <stdio.h>
#include <stdlib.h>
#include "src/c_header.h"

#define N 10
int main(void)
{
  int array[] = {1,2,3,4,5,6,7,8,9,10};

  int s = sum(array, sizeof(array)/sizeof(array[0]));
  printf("sum=%d\n",s);

  int* p = 0;
  int r = fib(&p,N);
  if(r==1){ printf("failed"); }
  else{
    int i;
    for(i=0; i < N; ++i)
    {
      printf("fib(%d)=%d\n",i,p[i]);
    }
  }
  free(p);
  return 0;
}

sumやfibは次のようにHaskellから扱える:

module Lib
    where

import Foreign.C.Types
import Foreign.Ptr
import Foreign.Marshal.Array
import Foreign

-- int sum(int* p, size_t n);
foreign import ccall "c_header.h sum" cSum
  :: Ptr CInt -> CSize -> IO CInt

hSum :: [Int] -> IO Int
hSum xs =
  let
    xs' = fromIntegral <$> xs :: [CInt]
    len   = fromIntegral $ length xs :: CSize
  in
  do
    arr <- newArray xs' :: IO (Ptr CInt)
    l <- return len  :: IO CSize
    ret <- cSum arr l :: IO CInt
    return $ fromIntegral ret

-- int fib(int** pp, int n);
foreign import ccall "c_header.h fib" c_fib
  :: Ptr (Ptr CInt) -> CInt -> IO CInt

hFib :: Int -> IO [Int]
hFib n =
    do
      ptrOut <- malloc  :: IO (Ptr (Ptr CInt))
      n' <- return $ fromIntegral n :: IO CInt
      ret <- c_fib  ptrOut  n' :: IO CInt
      out <- peekArray n =<< peek ptrOut :: IO [CInt]
      free =<< peek ptrOut
      free ptrOut
      return (fromIntegral <$> out) :: IO [Int]

cSum と hSum, c_fibとhFibのシグネチャは結構ずれている.
関数cSumは sum と同様にポインタとサイズを受け取る.hSum ではサイズを配列から計算しているので長さを引数にする必要がない.
また,c_fib では二重ポインタと長さを引数にしているが,hFibでは長さだけが引数となっている.c_fib の引数となっている二重ポインタは出力用の変数だからである.

次のコードはhSum,hFibの使用例である.

module Main where

import Lib

main :: IO ()
main =
  do
    a <- hSum [1,2,3,4]
    print a
    xs <- hFib 15
    print xs

stack でこのコードをビルドする場合,stack.yaml に次のような行を加えねばならない:

c-sources:  src/c_impl.c

optparse-generic

自分用メモ.

必要なことはすべて https://github.com/Gabriel439/Haskell-Optparse-Generic-Library から辿れるhttp://hackage.haskell.org/package/optparse-generic/docs/Options-Generic.htmlに載っている.

■optparse-genericとは何ですか?:コマンドライン引数を処理するライブラリの一つです.

■インストール:stack install optparse-generic

■サンプル:

{-# LANGUAGE DataKinds          #-}
{-# LANGUAGE DeriveGeneric      #-}
{-# LANGUAGE FlexibleInstances  #-}  -- One more extension.
{-# LANGUAGE OverloadedStrings  #-}
{-# LANGUAGE StandaloneDeriving #-}  -- To derive Show
{-# LANGUAGE TypeOperators      #-}

import Options.Generic --stack install optparse-generic

data OptionInput  w = OptionInput
  { foo :: w ::: Int           <?> "Documentation for the foo flag"
  , bar :: w ::: Double        <?> "Documentation for the bar flag"
  , baz :: w ::: String        <?> "Documentation for the baz flag"
  , qux :: w ::: Maybe String  <?> "Documentation for the qux flag"
  , quux :: w ::: Bool         <?> "Documentation for the quux flag"
  } deriving (Generic)

instance ParseRecord (OptionInput Wrapped)
deriving instance Show (OptionInput Unwrapped)

main = do
    x <- unwrapRecord "program short description"
    let i = foo x :: Int
    let d = bar x :: Double
    let s = baz x :: String
    let s' = qux x :: Maybe String
    let b = quux x :: Bool
    print (x :: OptionInput Unwrapped)

■サンプルのビルド:stack ghc optiontest.hs
必要に応じて,package.yaml で
dependencies:
– optparse-generic

■サンプルを動かす:optiontest.exe --foo 2 --bar 3.478 --baz "hoge" --qux "fuga"

github メモ

自分用メモです.

■開発作業
git checkout -b hoge #作業用ブランチを作成
★自由にコード等を編集する.
git commit -m "fix bug" #変更内容をコミット
git push origin hoge #自分のレポジトリにプッシュ
★github ページに言ってPull Request を作る.
★権威ある人に merge してもらう.

■自分のレポジトリを最新の状態に更新する
(git remote add upstream git@github.com:foo/bar.git)← upstream の登録は一度やればいい
git checkout master #作業ブランチを master に
git pull upstream master
git push origin master #自分のレポジトリにpush

■開発ブランチにmasterの内容を適用する
git checkout master
git pull origin master
git checkout hoge
git rebase master

★叱られたらファイルを修正して
git rebase –continue

■何がうまくいかない場合とりあえず現状を把握する
git status

■ローカルでの変更が邪魔して pull できない場合
git stash save "some-name"

■作業ブランチの確認
git branch

■ブランチ一覧
git branch -a

■リモートブランチの一覧
git branch --remote

■リモートブランチ foo の削除
git push origin :foo

■作業ブランチをhogeに変更
git checkout hoge

■作業ブランチの名称を fuga に変更
git branch -m fuga

■手元のブランチ piyo をリモートに登録する
git push -u origin piyo

■git管理下にあるファイル一覧
git ls-files

■git管理下にあるファイルをすべてadd
git add .

■git管理下にあるファイルのリネーム
git mv aaa.txt bbb.txt

■出したプルリクを取り下げる
remoteにpush済みのブランチを削除することで紐づくpull requestを削除出来ます
git push --delete origin hoge

■url の確認
git remote -v

■git管理下にあるファイルを削除
git rm hogehoge
(ディレクトリごと:git rm -r hogedir )

■ファイルをローカルに残したままgit管理下からファイルを除く
(1) git rm --cached hogehoge
(2) .gitignore に追記する

■originの変更
git remote set-url origin git@github.com:foo/bar.git

■upstreamの変更
git remote set-url upstream git@github.com:foo/bar.git

■自分がコミットしたところだけログを見る
git log --author=differential.engine@gmail.com

こうやって
commit 23985b88d43328a72bacafb784d7e30f78357f83
Author: dif_engine
Date: Fri Jul 6 23:00:00 2018 +0900

だったとき
git diff 23985b88d43328a72bacafb784d7e30f78357f83 > hoge.txt
とするとコミット 23985b88d43328a72bacafb784d7e30f78357f83 から HEAD までの差分が手に入る.一般には
git diff COMMIT-ID1:COMMIT-ID2 > out.txt

latexのソースからMeCabを用いて名詞だけ抜き出す

■記事の概要:latexのソースから形態素解析器を用いて名詞だけ抜き出したリストを作る.
■環境:MinGW または MSYS2 (Cygwin でも多分大丈夫だろうな~と思うが試していない)
■主な道具:MeCab, python3, make

■動機と背景:latexで作成しているPDFファイルのページ数が増えてきたので,索引を付けようと考えた.(索引はlatex の\indexという命令で作れるが,この記事ではこれ以上触れない).最初は索引の項目を手で付けていたが,思いついたものを十個ぐらい挙げたら飽きてしまった.そこで,《latexソースファイルの中から名詞だけ抜き出す》ことを考えた.

■環境整備
1.MinGW または MSYS2 については環境整備が済んでいるものとする.
2.MeCabのインストール:Windows用に配布されているバイナリを使う.公式からリンクされているダウンロードページ(http://taku910.github.io/mecab/#download)からBinary package for MS-Windowsという項目の直下の mecab-0.996.exe をダウンロードし,インストールした.インストール時にエンコーディングを指定するように言われる.今回の目的はlatexのソース処理なので utf-8 を選ぶ.(デフォルトだとShift-JPだったように記憶している).
3.Pythonのインストール:Windows用に配布されているバイナリを使う.公式ページ(https://www.python.org/)からDownload/latestに飛び,そこから「Windows x86-64 executable installer」と題されたファイルをダウンロードし,インストールした.この記事が書かれた時点での最新版は3.6.4であった.
4.パスを通す.MecabとPythonがMinGWシェルから見えるようにパスを通す必要がある.(MinGW なら
C:\MinGW\msys\1.0\home\YourUserName, MSYS2 なら C:\msys64\home\YourUserName などに置いてある).bashrc の下の方に次のような行を加える:

export MECABPATH=/c/MeCab
export PATH=$PATH:$MECABPATH/bin

export PYTHONPATH=/c/Python3.6.4
export PATH=$PATH:$PYTHONPATH
export PYTHONIOENCODING=UTF-8

※《/c/MeCab》や《/c/Python3.6.4》は各自のインストールの実態に合わせて適宜変更してほしい.
※最後の《export PYTHONIOENCODING=UTF-8》という行はパスを通しているのではなく,ユニコード文字(utf-8)をpython3で扱うときに必須らしい設定である.

■MeCab用のユーザー辞書作成
MeCab には「微分」や「積分」のような一般的な用語は(Windowsバイナリに同梱の)辞書ファイルに登録されている.しかし,それほど一般的でない用語は登録されておらず,バラバラの言葉や文字として認識されてしまう.そこでユーザ辞書を作成する.ユーザ辞書の元になるデータは次のような形式のcsvファイルである:

無限小,,,10,名詞,一般,*,*,*,*,個別名,アアアア,REGISTERED
無限大,,,10,名詞,一般,*,*,*,*,個別名,アアアア,REGISTERED
無限小量,,,10,名詞,一般,*,*,*,*,個別名,アアアア,REGISTERED
無限大量,,,10,名詞,一般,*,*,*,*,個別名,アアアア,REGISTERED

「アアアア」などとなっている箇所は本来きちんとした読みを登録するのだが面倒であり,今回の業務と無関係なのでこのようにしてある.(今回は説明を省略するが,一行に一つ単語を書いたファイルからこのようなファイルを自動生成している.)「REGISTERED」は後で解析結果を加工するためのヒントとするための追加タグである.

■Makefileの作成
次のようなMakefileを用意する.

help:
	@grep -E '^[0-9a-zA-Z_-]+[[:blank:]]*:.*?## .*$$' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[1;32m%-30s\033[0m %s\n", $$1, $$2}'

dict : mecab.csv ## add user dictionary for mecab by mecab.csv
	mecab-dict-index -d$(MECABPATH)/dic/ipadic -u ./mecab.dic -f utf8 -t utf8 mecab.csv

mecab : hoge.tex ## analyze hoge.tex by mecab
	$(eval MECAB_OUT := $(strip $(basename .tex $ $(UNIQ)
	python extractNoun.py  $(UNIQ) > $(RESULT)
	rm  $(MECAB_OUT) $(UNIQ)

make dict としてやると mecab.csv が処理され mecab.dic というMeCab用ユーザー辞書が作成される.(helpターゲットのルールについては以前の記事を参考にしてほしい).

make mecab としてやるとまずターゲットの hoge.tex が MeCab で処理され,hoge-out.txt が生成される.このファイルはhoge.texのすべての内容を品詞分解して一行に一つずつ並べたものになっているので重複が多い.そこでhoge-out.txt sort し uniq して重複を除去して hoge-out-u.txtを生成する.(前段の sed については以前の記事を参考にされたい.)今回は「名詞」とタグ付けされたものだけ欲しいので,hoge-out-u.txt を自作のpythonスクリプト extractNoun.py で処理して最終結果の hoge-nouns.txt を得る.

★Makeルールの中で変数を定義する方法については『makefile 動的に変数に代入 共通して使う』を参考にさせて頂いた.

■pythonスクリプト extractNoun.py

# -*- coding: utf-8 -*-
import sys
import re #正規表現
import codecs
import copy

reNegIgnoreFlag = re.compile('REGISTERED')
reNeg = re.compile('接頭詞')
rePos = re.compile('(.+?)名詞')
reNum = re.compile('(\d+)')
reSymbols = re.compile('[!\$%\#&\(\){}\+,\-@\.\\\\=\"\'\*:;\?–\[\]|_`\^,]+')

def main():
    f = open(sys.argv[1], 'r', encoding="utf-8_sig")
    input = f.readlines()

    for line in input:
        myFilter(line)

def myFilter(line):
    pos = rePos.search(line)
    neg1 = reNeg.search(line)
    neg2 = reNegIgnoreFlag.search(line)

    if pos and (not neg1) and (not neg2):
        result = pos.group(1).strip()
        isnumber = reNum.search(result)
        containsSymbols = reSymbols.search(result)
        if (not isnumber) and (not containsSymbols):
            sys.stdout.write(result)
            sys.stdout.write('\n')

if __name__ == '__main__' : main()

解説:関数 main() では,一番目の引数 sys.argv[1] として与えられたファイル名を持つファイルを開き,readlines で行の集まりに分解し,各行を関数 myFilter() で処理している.
関数 myFilter() では,事前にコンパイルされた5つの正規表現 reNegIgnoreFlag,reNeg,rePos,reNum,reSymbols を使って「欲しそうなものを抜き出し,要らないものを弾く」ことをやっている.それぞれの正規表現の目的は次の通り:

・reNegIgnoreFlag:MeCabのユーザー辞書に登録済みの単語を検出するため.(MeCabのユーザー辞書のcsvファイルは索引づくりのヒントファイルを兼ねており,そこにまだ登録されていない単語を探すのが今回の目的なので).
・reNeg:「名詞」タグが付いているもののうち,さらに「接頭詞」というタグが付いたものを排除するため.このタグがつくものは「不」「無」「第」「最」などの一文字単語(?)であり,今回の目的にはノイズなので弾く.
・rePos:「名詞」タグを検出するため.
・reNum:年号などの数字列を弾くため.
・reSymbols:latexソースを自然言語解析器にかけた結果として大量の無意味記号列が(MeCabによって,仕方なく)「名詞」に分類されてしまうためそれらを弾くため.

git で特定のファイルだけ取ってくる(自分用メモ)

git checkout fname

commit 前のファイルを git stash で上書きしてしまった!どうする?(自分用メモ)

要約
git stash pop が使えないか検討しよう.

背景
共同作業しているプロジェクトで,「どれどれ,最近の他の人の作業どうなってるかな」という軽い気持ちで git pull したあなたは pull が失敗することに気づいた.「よくわかんねえな」と呟いたあなたはさらに何気なくgit stash してしまう.とりあえずチームの作業を確認したあなたは,作業中だったエディタに向かった.そして異変に気づく:「あれ…このファイル,前回の commit の状態に戻されてるぞ.」そう,あなたが前回の commit から数日間の間に行った大量の変更は消し去られ,数日前の状態に戻ってしまったのだ.

対処
“stash”というのは,何かをこっそり隠すだとか取り除けておく,というような意味合いの言葉です.
その名前が示唆するように,git stash したとき,先程まで保存されていたファイルは(保存されていなかったとしたら流石にアウトだと思うので,エディタが自動的に保存しているバックアップなどを探すしかないでしょう)どこかに置かれています.

最後の stash で退避したファイルを戻すには git stash pop すれば良いようです.

習慣による回避
このような回復手段が git に備わっていることは大変素晴らしいことですが,驚いたり焦ったりすると利用可能な思考のリソースが削がれてもったいないので,日頃から些細な変更のあとにも躊躇なく commit するといいんだろうな~.

2018/01/01 追記
矢澤にこエヴァンジェリストさん情報:git stash save "hoge"のようにすると良いのかもしれない

山本悠滋‏さん情報:stash@{1} みたいに指定すればdiffも見れたはずです

時間があるときに調べて書き足すこと
git stash list とかしたときに幾つか stash の結果が出てくるけど,0が一番新しいやつなのか?とかよくわからなかった.stash を複数回やってしまった場合などに回復するためには stash list で得られたリストの時系列をちゃんと知っておくべき.

atom で non-greedy な正規表現を使う(自分用メモ)

■短い答え: ?をうまく使え。
古い論文をOCRで処理している。するとOCRのミスで
“apple’
というようなものが出てきた。これが意図通りなら問題ないが本来は
“apple”
となって欲しい。こういう例が随所にあるので一括して処理するために正規表現を使おうと思った。

■最初に
"(.+)'
を試したが、正規表現がgreedy、つまり、この表現にマッチする最大長の文字列を探してしまうため、得られたテキストデータが「一段落につき改行一つ」だったりすると大変困る。そこで、non-greedy なマッチをさせる ? をつかって
"([^"]+?)'
のようにした。ここで[^"]を使っているのは
“I have a pen,” he said. Soon after that, he stook the pen to the apple, and said “apple-pen’.
みたいな文章が全文選択されるのを防ぎたいから。

■non-greedy なオプションは他の正規表現エンジンでも使えることが増えてきてる気がするけど、atom 以外でどうなってるかは知らない。

VMware Tools の手動アップグレード(自分用メモ)

ヘルプページには初回用の操作も書いてあって説明が分岐して読むのが疲れるので、手動アップグレードの手続きを自分用にメモしておく。

$ sudo su
[sudo] uname のパスワード:
# mount /dev/cdrom /mnt/cdrom
# cd /tmp
# rm -rf vmware-tools-distrib
# ls /mnt/cdrom

VMwareTools-xxx-yyy.tar.gz が見えてるはず

# tar zxpf /mnt/cdrom/VMwareTools-x.x.x-yyyy.tar.gz
# cd vmware-tools-distrib
# ./vmware-install.pl

質問されたら全部Enterで返事しておく。

msys2 上で ocaml をビルドしようとして失敗した記録

■真新しいmsys2上でomakeを使いたくなったが、周知の通り現在はWindows用のバイナリが配布されていない。
msys2上のocamlでomakeをビルドしようとしたが上手く行かない。そこで、msys2上でocamlをビルドしてみればいいのではないかというアホなことを考えて失敗した記録。

■準備。本当に必要かどうかはわからない。

$ pacman -S gamin #fam みたいなもの
$ pacman -S bison #yacc みたいなもの
$ pacman -S gcc git patch

■ocamlをビルドする

#ocaml を msys2 上でビルドする。
$ git clone https://github.com/ocaml/ocaml
$ cd ocaml
$ ./configure -host x86_64-w64-windows -cc "gcc -Wno-error=implicit-function-declaration -Wno-error=int-conversion -Wl,--stack,16777216"
$ make clean
$ make world
$ make install

ln: シンボリックリンク 'ocamlyacc' の作成に失敗しました: No such file or directory
make: *** [Makefile:606: install] エラー 1

■msys2でsymlinkを使う方法:msys2_shell.cmd をテキストエディタで編集し

rem To activate windows native symlinks uncomment next line
rem set MSYS=winsymlinks:nativestrict

となっているところを

rem To activate windows native symlinks uncomment next line
set MSYS=winsymlinks:nativestrict

に変更して保存する。そして、msys2_shell.cmd を右クリックして「管理者として実行」してmsys2のシェルを立ち上げる。

■手持ちの別のマシンでやったところ ocamlyacc のエラーは出なかったが omake のビルドで

$ ocaml configure.ml
Cannot load required shared library dllcamlstr.
Reason: dllcamlstr.so: dynamic loading not supported on this platform.
Cannot load required shared library dllunix.
Reason: dllunix.so: dynamic loading not supported on this platform.
File "./configure.ml", line 1:
Error: Reference to undefined global `Str'

となって止まってしまった。