zsh 用のクロージャのようなものはありますか?

zsh 用のクロージャのようなものはありますか?

私は zsh (oh-my-zsh 経由) を試してみることにし、precmd最後の行だけでなく複数の行に正しいプロンプトがある 2 行のプロンプトをエミュレートしようとしています。

そこで私はデフォルトのテーマを複製し、この郵便受け(私も多くのことを学ぶために使っています)、私は次のようにします (後で色を追加します):

function precmd {
    local cwd="${(%):-[%~]}"
    local who_where="${(%):-%n@%m}"
    local git_info=${(%)$(git_prompt_info)}
    local right_prompt="     $git_info [$who_where]"
    local left_prompt="${(r:(($COLUMNS - ${#${right_prompt}})):: :)cwd}"

    echo "$left_prompt$right_prompt"
}

そして、それは動作します。しかし、私は疑問に思わずにはいられません。zsh は、precmd が呼び出されるたびに、これらすべての変数を定義しているのでしょうか?

私は、zsh に関連するクロージャ、スコープ、および名前空間について Google で検索し、ローカル変数をデータとして precmd に添付して、毎回変数を再定義する必要がないようにしようとしましたが、何も見つかりません。私が試していることを実行する方法はありますか、それとも、それをやめたほうがよいでしょうか?

補足として、関連している場合に限りますが、「関数をロードする」とはどういう意味ですか?

答え1

Zsh にはクロージャやパッケージ、名前空間のようなものはありません。Zsh には真のクロージャに必要な多くのものが欠けています。

  • 関数はファーストクラスではありません。関数を他の関数の引数として渡すことはできませんし、関数が他の関数を返すこともできません。(名前呼び出す関数を指定しますが、関数自体を渡すのとは異なります。

  • ネストされた関数を持つことはできません。zsh のすべての関数はグローバルです。競合を避けるために、関数名にプレフィックスを付ける必要があります。特に、関数は同じ名前の外部プログラムをシャドウすることに注意してください。 という関数がある場合ls、プログラム の代わりにそれが呼び出されますls。これは、誤って行った場合を除き、便利です。

  • 変数は動的スコープを持ち、ほとんどの最新言語のように静的スコープを持ちません。ネストされた関数を持つことができたとしても、内部関数は、通常期待されるような方法で外部関数のローカル変数を閉じることはありません。Javascript などで人々が行うような方法で、それらを使用してモジュールを作成することはできません。

  • ズッシュする匿名関数がありますが、これら他の機能がない場合は、あまり役に立ちません。

したがって、基本的には、すべての関数とグローバル変数にプレフィックスを付けるのが最適です。

precmdまた、次のように定義する必要があることも指摘しておきます。

% autoload -Uz add-zsh-hook
% add-zsh-hook precmd my_precmd_function

add-zsh-hookprecmdを使用すると、 をフックする必要がある他の関数を上書きすることなく、 をフックできますprecmd

関数がロードされるということはどういうことかは別の問題です。Zsh には、autoload -Uz foobar関数が実際に呼び出されたときにのみディスクから関数をロードする自動ロード機能があります。 を実行すると、指定された関数がfoobar呼び出し可能になります。 を実際に呼び出すとfoobar、ディスクから定義がロードされます。

答え2

いいえ、クロージャは zsh には複雑すぎます。zsh は、直接対話からそれほど離れていない小さなスクリプトを解釈するように設計されています。大規模なプログラミングには非常に便利ですが、シェルが通常使用されるような小さなタスクにはあまり役立たない、高度な言語機能はありません。

変数の値を一度だけ事前に計算して保存できる何らかの形式のクロージャがあった場合、何かが変更されて情報が無効になったときに、値は更新されないことに注意してください。

$git_infoまた、派生変数は、git にチェックインされたファイルまたは git リポジトリの変更によりいつでも変更される可能性があります。そのため、いずれにしても毎回再計算する必要があります。

cwdおよびの値はwho_where、通常の操作では変更されないため、グローバル変数にキャッシュできます。cwdは、現在のディレクトリが変更されると変更されるため、 から更新する必要がありますchpwd。ただし、これらの変数の計算は非常に高速であるため、気にする必要はありません。ここでのコストの高い計算は の実行でありgit_prompt_info、これはいつでも変更される可能性があります。

PS1各コマンド間で情報を表示する場合は、プロンプト (または配列)の一部として配置する方がよい場合がありますpsvar。Zsh は、さまざまな状況でプロンプトを再表示する必要があることを認識していますが、 から何を印刷するかについては何も認識していませんprecmd

答え3

はい、これらの変数は関数を呼び出すたびに(再)定義されます。

一度だけ初期化したい場合は、関数外の最上位レベルに移動するだけです。

答え4

クロージャを持つには、言語が関数を要素またはオブジェクトとして操作できる必要がありますが、zsh では文字列とeval組み込み関数 (他のシェルと同様) 以外ではそれができません。しかし、低レベルの処理 (引用符など) はすべて自分で処理する必要があるため、これは非常に制限されています。ただし、引数に特殊文字が含まれていない場合 (したがって引用符を処理する必要がない場合)、このアイデアを使用していくつかの簡単な処理を簡単に実行できます。zsh では、匿名関数が少し役立ちます。たとえば、 を計算する関数を定義するにはa*x+b*yaと はb関数の定義時に提供される定数であり、xy関数の引数です。

mk_ax_plus_by() { echo "() { echo \$((($1)*(\$1)+($2)*(\$2))) }" }

fct_2x_plus_3y=$(mk_ax_plus_by 2 3)
fct_5x_plus_7y=$(mk_ax_plus_by 5 7)

fct_2x_plus_3yつまり、計算する関数と計算する2*x+3*y関数が 1 つずつあります(関数名は読みやすさのために選択しただけであり、任意の名前にすることができ、内容を変数に格納する必要もありません)。また、これらは実際には文字列 (シェル用語では関数ではありません) ですが、組み込み関数のように動作します。使用例:fct_5x_plus_7y5*x+7*yeval

% eval $fct_2x_plus_3y 4 9
35
% eval $fct_5x_plus_7y 4 9
83

2*4+3*935 を返し、5*4+7*983 を返します。

関数型言語とは異なり、ここでは環境から来るパラメータ (引用符なし) と定義された関数へのパラメータ (引用符付き) を区別する必要があることに注意してください。つまり、関数呼び出し時にのみ評価されます。ただし、実装を関数型言語のように変更することは可能であり、使用方法も同じになります。

mk_ax_plus_by()
{
  local x='$1' y='$2'
  echo "() { echo \$((($1)*($x)+($2)*($y))) }"
}

関連情報