Existe algo como fechamentos para zsh?

Existe algo como fechamentos para zsh?

Eu apenas decidi tentar o zsh (por meio do oh-my-zsh) e agora estou brincando precmdpara emular um prompt de duas linhas que possui prompts corretos em mais do que apenas a última linha.

Então eu clonei o tema padrão e inspirado poresta postagem(que estou usando para aprender bastante também), faço algo assim (adicionarei cores mais tarde):

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"
}

E funciona. Mas não posso deixar de me perguntar: o zsh está definindo todas essas variáveis ​​toda vez que o precmd é chamado?

Estive pesquisando no Google por encerramentos, escopo e namespace em relação ao zsh, procurando anexar os vars locais como dados ao precmd, para que não seja necessário redefinir as variáveis ​​todas as vezes, mas não encontrei nada. Existe alguma maneira de fazer o que estou tentando ou devo simplesmente desistir?

Como observação lateral, e somente se estiver relacionado, o que significa "ter uma função carregada"?

Responder1

Zsh não tem nada parecido com encerramentos, pacotes ou namespaces. O Zsh não possui um monte de coisas necessárias para ter fechamentos verdadeiros:

  • Funções não são de primeira classe. Você não pode passar funções como argumentos para outras funções, e funções não podem retornar outras funções. (Você pode passar onomede uma função a ser chamada, mas isso não é o mesmo que passar a função em si).

  • Você não pode ter funções aninhadas. Todas as funções no zsh são globais. Você deve prefixar os nomes das funções para evitar conflitos. Observe especialmente que as funções ocultarão programas externos com o mesmo nome. Se você tiver uma função chamada ls, ela será chamada em vez do programa ls. Isso pode ser útil, exceto se você fizer isso por acidente.

  • As variáveis ​​têm escopo dinâmico, não estaticamente como na maioria das linguagens modernas. Mesmo se você pudesse ter funções aninhadas, as funções internas não fechariam as variáveis ​​locais das funções externas da maneira que você normalmente esperaria. Você não poderia usá-los para criar módulos como as pessoas fazem, digamos, em Javascript.

  • Zshfaztêm funções anônimas, mas sem nenhuma dessas outras coisas elas não são muito úteis.

Então, basicamente, o melhor que você pode fazer é prefixar todas as suas funções e variáveis ​​globais.

Também salientarei que você deve definir o seu precmdassim:

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

add-zsh-hookpermite que você conecte sua função precmdsem sobrescrever quaisquer outras funções que também possam querer conectar precmd.

O que significa ter uma função carregada é uma questão separada. Zsh possui um recurso de carregamento automático que carrega apenas funções do disco quando elas são realmente chamadas. Quando você faz autoload -Uz foobarisso, a função nomeada fica foobardisponível para chamada. Quando você realmente chama foobar, isso carrega a definição do disco.

Responder2

Não, os encerramentos são muito sofisticados para o zsh. Zsh foi projetado para interpretar pequenos scripts que não estão muito distantes da interação direta. Ele não possui recursos de linguagem sofisticados que são muito úteis para programação em grande escala, mas nem tanto para o tipo de tarefas pequenas para as quais os shells são normalmente usados.

Observe que se houvesse alguma forma de fechamento que permitisse que o valor das variáveis ​​fosse pré-calculado de uma vez por todas e depois armazenado, os valores não seriam atualizados quando algo mudasse e tornasse a informação inválida.

$git_infoe as variáveis ​​derivadas podem mudar a qualquer momento devido a uma modificação em um arquivo verificado no git ou no repositório git. Portanto, eles precisam ser recalculados todas as vezes.

Você poderia armazenar em cache os valores de cwde who_whereem uma variável global, já que eles não mudam sob operação normal. cwdmuda quando o diretório atual muda, então precisaria ser atualizado de chpwd. No entanto, essas variáveis ​​são extremamente rápidas de calcular, então não há motivo para se preocupar. A computação cara aqui está em execução git_prompt_infoe isso pode mudar a qualquer momento.

Ao exibir informações entre cada comando, pode ser uma ideia melhor colocá-las como parte do prompt ( PS1ou da psvarmatriz). Zsh sabe que deve exibir novamente o prompt em diversas circunstâncias, mas não sabe nada sobre o que você imprime precmd.

Responder3

Sim, essas variáveis ​​são (re)definidas toda vez que você chama a função.

Se quiser inicializá-los apenas uma vez, você pode simplesmente movê-los para o nível superior, fora da função.

Responder4

Para ter fechamentos, a linguagem precisa ser capaz de manipular funções como elementos ou objetos, o que não é possível no zsh, exceto via strings e o evalbuiltin (como nos outros shells). Mas isso é muito limitado, pois você mesmo precisa lidar com todas as coisas de baixo nível (por exemplo, citações). Porém, quando os argumentos não possuem caracteres especiais (portanto, não é necessário lidar com aspas), é facilmente possível fazer algumas coisas simples usando essa ideia, e no zsh, funções anônimas podem ajudar um pouco. Por exemplo, para definir funções que computam a*x+b*y, onde ae bsão constantes fornecidas na definição da função xe ysão os argumentos da função:

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)

Portanto, temos uma função fct_2x_plus_3yque calcula 2*x+3*ye uma função fct_5x_plus_7yque calcula 5*x+7*y(observe que escolhi os nomes das funções apenas para facilitar a leitura, podem ser quaisquer nomes e você nem precisa armazenar o conteúdo em variáveis). Observe também que na verdade são strings (não funções na terminologia do shell), mas se comportarão como funções com o evalbuiltin. Um exemplo de uso:

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

como 2*4+3*9dá 35 e 5*4+7*9dá 83.

Observe que diferentemente das linguagens funcionais, aqui é necessário diferenciar parâmetros que vêm do ambiente (sem aspas) e parâmetros da função definida (entre aspas), ou seja, que serão avaliados apenas na chamada da função. Mas é possível mudar a implementação para ficar mais parecida com linguagens funcionais, e o uso será o mesmo:

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

informação relacionada