Git Aliasで引数を受け取る

Gitで引数を受け取ることのできるAliasの書き方があるようなので、紹介しておきます。

mikebarkas.dev

基本形は以下の通り。

[alias]
  echo = "!f() { echo ${1}; }; f"

このように~/.gitconfig記載すると、

$ git echo foo
foo

Aliasの先頭に!を付けるとシェルコマンドとして認識されるので(つけないとpullcommitなど、gitコマンドのサブコマンドと認識されます)、つまり上記はシェル関数を定義して即時実行するという仕組みです。 考えてみればシンプルですが、これは物凄く応用範囲が広くて便利です。 たとえば、以下のような感じに使えます。

[alias]
  ls-repos = "!find . -type d -name .git | xargs dirname"
  for-all = "!f() { for DIR in $(git ls-repos); do git -C $DIR $@ & done; wait; }; f"

ls-reposでカレントディレクトリ下のすべてのローカルリポジトリを列挙するエイリアスです。 for-allは、このls-reposで列挙した各ローカルリポジトリgitコマンドを実行します。 このときサブコマンドを引数として与えているので、任意の操作を行うことができます。

$ ls
repo-1 repo-2 repo-3
$ git for-all pull
Already up to date.
Already up to date.
Already up to date.

みたいな感じ。

個人的には、Go Workspaceで複数リポジトリを使用しているのでこれが便利です。

ちなみに以上はGit AliasのシェルがBashの場合です。Zshだとシンプルにecho = "(){ echo ${1} }"という感じに書けるとかなんとか。

NuxtJS + Sass(SCSS)でStylelint v14

Stylelint v14から破壊的変更が入り、Sassなどのルールはプラグイン化されました。 それに伴って、すでに導入済みの設定がある場合、いくつかの変更を加える必要があったので記録しておきます。

公式の移行ドキュメントは以下です。

https://stylelint.io/migration-guide/to-14/

結論を先に書くと、追加が必要な依存は以下です。

yarn add -D postcss postcss-scss
yarn add -D stylelint-config-standard-scss
yarn add -D postcss-html

で、stylelint.config.jsを以下のように修正します(元ファイルはプロジェクト次第なので、あくまで簡略版)。

 module.exports = {
-  extends: ['stylelint-config-standard', 'stylelint-config-prettier'],
+  extends: ['stylelint-config-standard-scss', 'stylelint-config-prettier'],
   rules: {
     'selector-pseudo-element-no-unknown': [
       true,
      {
        ignorePseudoElements: ['v-deep'],
      },
     ],
   },
+  overrides: [
+    {
+      files: ['**/*.vue'],
+      customSyntax: 'postcss-html',
+    },
+  ],
 }

まずpostcssですが、これはSassとかLessを使う場合は基本的に必要となる模様。 SCSSを使いたいのでpostcss-scssも一緒に追加しています。

stylelint-config-standard-scss は、今回からプラグイン化されたSCSSのルールです。 インストールしたらstylelint.config.jsのextendsに追加します。 これは公式ドキュメント通り。

最後にpostcss-htmlは、<style>タグに挟まれた部分だけチェックするようにしてくれるものです。 NuxtJS (Vue.js)特有の事情として、*.vueファイル中のスタイルは<style>タグ内に書くため、これが必要になります。 *.scssファイルなどでは不要なので、stylelint.config.jsのoverridesで*.vueファイルにのみ適用されるよう設定します。

以上です!

Fedora 35にFlutterをインストール

前提

Android StudioをFlatpak経由でインストール済みの場合を想定しています。 FlatterはSnap経由でインストールするのが簡単そうなのですが、すでにFlatpakを使っており、これ以上パッケージマネージャーを増やしたくないし、このためだけに乗り換えるのもなんだかなあという人の参考になればと思います。

大筋は以下に従います。 docs.flutter.dev

依存関係のインストール

公式によるとClang, CMake, GTK development headers, Ninja build, pkg-configが要るとのことですが、pkg-configはデフォルトで入っているようなのでそれ以外をインストールします。

sudo dnf install ninja-build cmake clang gtk3-devel

Flutterのインストール

ホームにbin/というディレクトリを掘って、そこにgit cloneします。 どこでもいいのですが、普段から~/binに自作スクリプト類を色々と置いてるのでそこに同居させています。 全ユーザーが使うなら/opt/などがいいと思います。

mkdir ~/bin
cd ~/bin
git clone https://github.com/flutter/flutter.git

PATHを通します。 .bashrcか.bash_profileに以下を追記してください。

export PATH="$HOME/bin/flutter/bin:$PATH"
source ~/.bash_profile

flatter doctorが通るようにする

まずはAndroid Studioを立ち上げ、各種アップデートを完了しておいてください。

Android Studioが大人しくなったら、Flatpak経由で入れた場合のAndroid StudioのパスをFlatterに教えてあげます

flutter config --android-studio-dir=/var/lib/flatpak/app/com.google.AndroidStudio/current/active/files/extra/android-studio/

つぎに、Android SDKコマンドライン ツールを入れます。 Androidsdkmanagerコマンドを使えるようにするため、.bashrcか.bash_profileに以下を追記してPATHを通してください。

export PATH="$HOME/Android/Sdk/tools/bin:$PATH"
source ~/.bash_profile

今のsdkmanagerはJava8でないと動かないようなので、alternativesコマンドで切り替えておきます(Java8がインストールされていない場合は入れておいてください)。 以下のコマンドで使用するJavaのバージョンを選択できます。

sudo alternatives --config java

java --versionでJava8に切り替わってることを確認できたら、Android SDKコマンドラインツールのインストールを実行します。

sdkmanager --install "cmdline-tools;latest"

これでflutter doctorコマンドを実行して、全項目[✓]になればOKです!

$ flutter doctor
Doctor summary (to see all details, run flutter doctor -v):
[] Flutter (Channel master, 2.6.0-12.0.pre.789, on Fedora Linux 35 (Workstation Edition) 5.14.18-300.fc35.x86_64, locale ja_JP.UTF-8)
[] Android toolchain - develop for Android devices (Android SDK version 30.0.2)
[] Chrome - develop for the web
[] Android Studio
[] IntelliJ IDEA Community Edition (version 2021.2)
[] VS Code (version 1.62.3)
[] Connected device (1 available)

• No issues found!

Java 11に戻した上でflutter doctorしても通ったので、とりあえずこれでOKだと思います。

Fuzzy Containsについて

小ネタです。

かつてあいまい検索といえば、レーベンシュタイン距離などで語句同士の類似度合いを数値化して、域値で絞ったり類似度順に並べるのが普通だったように思います。

ja.wikipedia.org

ですが、数年くらい前からか、もっと簡単なあいまい検索をよく見かけるようになりました。 AtomVS CodeGitHubのファイル検索なんかで使われているあれです。

なんて名前なのかな?と思っていたのですが、Fuzzy Containsとか呼ばれているようです。

とても単純なロジックなのでサクッと書けます。 JavaScriptだと以下のような感じ。

function fuzzyContains(query, target) {
  if (query.length > target.length) return false;
  for (let i = 0, j = 0, l = query.length; i < l; i++, j++) {
    j = target.indexOf(query[i], j);
    if (j === -1) return false;
  }
  return true;
}

結果は文字が順番通りに含まれているか否かという二値だけですが、場面によってはこれが類似度を使うよりも直感的で使いやすいように感じます。 気に入ったのでいろいろなところで使っていきたいと思います。

ホーナー法について

たいしたことないネタでもちょいちょい記事を書いてみることにします。今日はホーナー法について。

ホーナー法 - Wikipedia

www.ndl.go.jp

多項式を計算する際、乗法の回数を削減することができます。上記リンク先によると、19世紀からある方法のようです。

やり方はとてもシンプルです。以下のような多項式があるとします。

\displaystyle
p(x) = a_0 + a_1x+a_2x^{2}+ \cdots +a_nx^{n}

これを、次のように変形するだけです。

\displaystyle
p(x) = a_0 + x(a_1 + x(a_2 + \cdots x(a_{n-1} + a_n x)\cdots))

JavaScriptで実装すると、以下のような感じになります。

var horner = (coeffs = [], x) => coeffs.reduce((a, b) => a * x + b, 0);

もしくは、簡潔さより高速さということであれば、以下のような感じでしょうか。

var horner = (coeffs = [], x) => {
  var r = coeffs[0];
  for (let i = 1, l = coeffs.length; i < l; i++)
    r = r * x + coeffs[i];
  return r;
}

第一引数には、[a_n, a_{n-1},\cdots,a_0]の順に係数の配列を渡します。

実際のところ、べき乗の計算は効率的なアルゴリズムが知られているため、素直に展開形を実装するのに比べて、ホーナー法でそこまでの高速化は期待できません。 ただ、特に小数がある場合、計算精度の向上を望めます。

また、FMA (Fused Multiply-Add) をサポートしている環境では、更なる精度向上が期待できそうです。 例えばJuliaはfma()関数を呼び出すことで、CPUが対応していればFMAを使うことができます。

function horner(coeffs, x)
  r = coeffs[1]
  for i in 1:length(coeffs)
    r = fma(r, x, coeffs[i])
  end
  r
end

FMAについては、以下のブログ記事が参考になりました。

math-koshimizu.hatenablog.jp

ちなみに、JuliaについてはBase.Math中に@horner(x, p...)というマクロが予め定義されているようです。

github.com

以下のようにして呼び出すことができます。

julia> using Base.Math: @horner
julia> a0, a1, a2 = 0.123, 0.234, 0.345
julia> x = 0.999
julia> @horner x a0 a1 a2
0.701076345

とても簡単に使えるアルゴリズムなので、ぜひ活用したいです。

以下の本を参考に書きました。

[改訂新版]C言語による標準アルゴリズム事典 (Software Technology)

[改訂新版]C言語による標準アルゴリズム事典 (Software Technology)

出てきやすいパターン


この問題、なんとなく「10」でも「11」でも平均でみたら同じになりそうな気がしてしまいます。
せっかくなのでJavaScriptで計算実験してみました。

// コインを投げる
function tossCoin() {
  return Math.round(Math.random(), 1);
}

// パターンが出るまでコインを投げる
function tossUntil(pattern) {
  let r = '';
  do {
    r += tossCoin();
  } while (r.lastIndexOf(pattern) === -1);
  return r;
}

// n回試行して結果を配列に詰める
function genData(n, pattern) {
  const array = [];
  for (let i = 0; i < n; i++) {
    array.push(tossUntil(pattern).length)
  }
  return array;
}

// 平均を計算する
function average(array) {
  return array.reduce((x, y) => x + y, 0) / array.length;
}

// テストを実行する
function runTest() {
  const n = 10000;
  const E10 = average(genData(n, '10'));
  const E11 = average(genData(n, '11'));
  alert(`E(N10) = ${E10}\nE(N11) = ${E11}`);
}

runTest();

結果は次のようになりました。

E(N10) = 4.146
E(N11) = 6.039

なんとなく「10」は4回、「11」は6回に収束していきそうな感じがします。

以下のリンクを押すとお使いのブラウザから試せます。

Run Test

問題の出典は以下の本です。解法が気になる場合は読んでみてください。