Dígito redondo / truncado em string em zsh (ou com ferramenta externa)

3

Estou tentando fazer uma interface para bc para que possa ser usada intuitivamente e sem o incômodo de ficar "presa" nela. Eu não cheguei a testá-lo muito, porque fiquei preso em outro detalhe, a saber, como apresentar o resultado (que é, na minha opinião, uma string).

Arredondamento ou truncamento não importa, qualquer um está bem. Dê uma olhada abaixo e você entenderá imediatamente. Eu uso o zsh, mas uma ferramenta externa ficará bem, já que não usarei isso em nenhum momento ou contexto crítico, é apenas uma ferramenta de desktop.

calc () {
    result='bc <<EOF
    scale=3;
    $@
    EOF'
    echo ${result//%0/} # doesn't work; will only remove one zero
                        # also, if there are only zeroes, should
                        # remove dot as well - what about .333, etc.?
}

Editar

Estou muito impressionado com a solução abaixo, especialmente como o noglob fica longe com as citações!

Mas, o uso de um ponto para forçar o cálculo do ponto flutuante é algo que eu nunca vou lembrar (você não usa uma calculadora normal como essa). E é até um pouco arriscado, especialmente para cálculos quando não é óbvio que o ponto flutuante produziria um resultado completamente diferente (provavelmente o que você queria).

Além disso, os cálculos abaixo mostram uma saída pouco bonita (o real muito longo e o ponto final).

Talvez eu deva combinar isso (alguns deles) com a formatação de saída da resposta do @Gille? Quando eu fizer funcionar perfeitamente, postarei o resultado aqui. ( Editar: A resposta aceita é excelente. Leia os comentários para essa pergunta. responder também.)

calc () {
  echo $(($*));
}
alias calc='noglob calc'

calc 1./3
0.33333333333333331
calc 7.5 - 2.5
5.
    
por Emanuel Berg 01.03.2013 / 23:26

4 respostas

4

Usando a própria aritmética de zsh , você poderia fazer:

calc() printf '%.6g\n' $(($*))
alias 'calc=noglob calc'

Mas isso significaria que você precisaria inserir números como 123. para que eles fossem considerados como ponto flutuante e acionassem um cálculo de ponto flutuante.

Você poderia contornar isso anexando . a qualquer sequência de dígitos decimais que não faça parte de um número hexadecimal (ou número em outra base) ou de um nome de variável ou 12e-20 tipo números como:

setopt extendedglob
calc() printf '%.6g\n' $((${*//(#bm)(([0-9.]##[eE][-+][0-9]##|[[:alnum:]_#]#[.#_[:alpha:]][[:alnum:]_#]#)|([0-9]##))/$MATCH${match[3]:+.}}))
alias 'calc=noglob calc'

Nesse momento, você pode achar mais fácil usar bc e aparar os 0s seguintes.

Veja também awk :

calc() awk "BEGIN{print $*}"

que suporta menos operadores e funções matemáticas, mas pode ser suficiente para você.

    
por 03.03.2013 / 22:58
2

Se eu entendi corretamente, você deseja excluir zeros à direita e um ponto à direita. Nesse caso, se EXTENDED_GLOB estiver definido, você pode usar

${result//%.#0##/}

Ou seja: no final da string ( % ) corresponde zero ou mais pontos ( .# ) seguido por um ou mais zeros ( 0## ).

Mas isso retornará "" if result is 0 . Você pode fazer outra substituição em torno do primeiro, para restaurar o valor de retorno para 0 :

${${result//%.#0##/}:-0}
    
por 02.03.2013 / 00:43
2

bc pode imprimir resultados como inteiros longos ou decimais. Aqui está um script que une inteiros longos divididos em várias linhas juntos e remove os zeros à direita após os pontos decimais em decimais.

calc () {
  emulate -L zsh; setopt extended_glob
  local line
  bc <<EOF |
scale=3
$@
EOF
    while read line; do
      if [[ $line = *.* ]]; then
        print -r -- ${${${line%%0##}/#%0#./0}%.}
      else
        print -r -- $line
      fi
    done
}

O modo como é escrito é mais um exercício de manipulação de texto com substituições de parâmetros do que um modo realmente claro de decimais de belas impressões.

  • ${…%%0##} remove o sufixo mais longo que corresponde a 0## , ou seja, zeros à direita.
  • ${…/#%0#./0} define a string como 0 se consistir apenas ( #% prefixo no padrão em ${VAR/PATTERN/REPLACEMENT} ) de zeros opcionais ( 0# ) e .
  • ${…%.} retira um . , se houver.

Acho que dividir as etapas é mais claro.

if [[ $line = *.* ]]; then line=${line%%0##}; fi
if [[ $line = . ]]; then line=0; else line=${line%.}
print -r -- $line
    
por 02.03.2013 / 03:01
1

Experimente zcalc no zsh; Se você ainda não estiver carregando automaticamente todas as funções que vêm com o zsh, será necessário autoload zcalc primeiro. Acabar com bc , tem prompts, $ back-referências de saída, histórico de comandos, funções científicas, a capacidade de definir mais funções, etc.

Documentado em zshcontrib (1).

Desvantagem: ainda tem o problema "ints por padrão", portanto 3./5 ! = 3/5

    
por 04.03.2013 / 10:18

Tags