Implementar uma calculadora gráfica

12

Houve muitas perguntas envolvendo calculadoras; no entanto, não parece haver nenhum envolvimento na implementação de uma calculadora gráfica.

O desafio

Você deve escrever um programa completo que use várias fórmulas como entrada de STDIN e as represente graficamente em STDOUT. A entrada assumirá o formato f1(x)=x^2-x-1. Haverá um fseguido por um número de 0 a 9 (inclusive), seguido por (x)=, seguido pela fórmula para representar graficamente. Seu programa deve poder receber entradas, gráficos, receber mais entradas, gráficos etc.

Isso é código de golfe.

Seu gráfico deve ter o intervalo do eixo X de -5 a 5, com uma resolução de pelo menos um ponto a cada 1/2 unidade. Os requisitos do eixo Y são os mesmos. Isso pode parecer um intervalo pequeno comparado às calculadoras modernas, mas provavelmente será trivial em aumentar isso. O gráfico deve ter o eixo desenhado neles, com marcas de escala na forma de +números inteiros.

A fórmula deve ser avaliada com a ordem normal de operação. Não haverá assíntotas verticais / regiões indefinidas nessas fórmulas. A variável será sempre x. Se duas fórmulas forem inseridas com o mesmo número de equação, a mais antiga deve ser apagada e substituída pela nova fórmula. Fórmulas em branco devem ser avaliadas como zero. Como é provável que a fórmula nem sempre dê um bom múltiplo de 1/2, você deve arredondar para o 1/2 mais próximo.

Quando uma fórmula é representada graficamente, sua linha deve ser formada a partir do número da fórmula. Quando uma linha cruza um eixo, o eixo deve ser desenhado por cima. Quando duas linhas se cruzam, não importa qual é mostrado.

Exemplo de entrada

f1(x)=x+1

Resultado

          +       1
          |      1
          +     1
          |    1
          +   1
          |  1
          + 1
          |1
          +
         1|
+-+-+-+-+-+-+-+-+-+-+
       1  |
      1   +
     1    |
    1     +
   1      |
  1       +
 1        |
1         +
          |
          +

Entrada

f2(x)=(x^2)^0.25

Resultado

          +       1
          |      1
          +     1
          |    1
          +   1
          |  1
2222      + 1    2222
    222   |1  222
       22 + 22
         2|2
+-+-+-+-+-+-+-+-+-+-+
       1  |
      1   +
     1    |
    1     +
   1      |
  1       +
 1        |
1         +
          |
          +

Entrada

f1(x)=-x  

(observe que é aceitável que seu programa rejeite essa entrada e apenas exceto 0-x ou x * -1, mas isso deve ser documentado)

Resultado

1         +
 1        |
  1       +
   1      |
    1     +
     1    |
2222  1   +      2222
    2221  |   222
       22 + 22
         2|2
+-+-+-+-+-+-+-+-+-+-+
          |1
          + 1
          |  1
          +   1
          |    1
          +     1
          |      1
          +       1
          |        1
          +         1
PhiNotPi
fonte

Respostas:

5

Perl, 177 caracteres (+1 na linha de comando)

perl -nE 's!\^!**!g;s!x!(\$k/2-6)!g;s/\d.*=/;/;$f[$&]=$_;my%a;for$k(@x=2..22){$i=0;$a{int 12.5-2*eval}[$k-2]=$i++for@f}$p="|";$$_[10]=$p^=W,$a{12}=[$p."-+"x10],say map$_//$",@$_ for@a{@x}'

Por esse meta-tópico , acredito que isso contará como 178 caracteres no total.

Como a solução Ruby, também estou usando evale substituindo ^por **.

A análise de entrada é incrivelmente frágil e incrivelmente robusta ao mesmo tempo: f1(x)=pode ser escrita como f 1 ( x ) =ou foo 1 bar =até mesmo justa 1=, mas coisas muito estranhas podem acontecer se você a substituir fpor algo que não seja uma declaração Perl válida e sem efeitos colaterais. Você foi avisado.

Outros detalhes de interesse incluem a maneira como o eixo vertical é desenhado, que explora o fato de que o XOR bit a bit dos caracteres +e o |é W. Obviamente, isso não funcionará em sistemas EBCDIC.

A saída é renderizada em um hash de matrizes, não em uma matriz de matrizes - acontece que são necessários menos caracteres para truncar explicitamente as chaves de hash para números inteiros e, em seguida, fazer um loop sobre uma fatia de hash do que é necessário para garantir que uma matriz não seja indexado com valores negativos. Eu poderia raspar mais dois caracteres se não fosse a maneira irritante de Perl inttruncar valores negativos para zero, o que me forçou a numerar as linhas de saída de 2 a 22 em vez de 0 a 20 para evitar arredondar os artefatos na borda superior da área de saída.

Eu uso a conversão liberal de string para número do Perl na análise de entrada, onde uso a string inteira 1(x)=como um índice de matriz (ela é convertida em apenas 1).

Também pude salvar mais três caracteres (e tornar a análise um pouco mais robusta) substituindo s/\d.*=/;/;$f[$&]=$_por /\d.*=/;$f[$&]=$', mas então eu teria que gastar o mesmo número de caracteres extras para escrever $'como $'\''em uma string de shell com aspas simples. Suponho que tecnicamente não precisaria contar isso, mas parece meio que trapaça.

Ilmari Karonen
fonte
6

Ruby, 200 caracteres

f={}
r=0..20
(f[gets[1]]=$_[6..-1].gsub /\^/,'**'
s=r.map{' '*21}
f.map{|n,k|r.map{|y|x=y*0.5-5
v=(2*eval(k)).round
v.abs<11&&y!=10&&s[10-v][y]=n
s[y][10]='+|'[y%2]
s[10][y]='+-'[y%2]}}
puts s)while 1

Uma implementação simples de ruby ​​usando o avaliador padrão para expressões ( ^será substituída para que os exemplos fornecidos acima funcionem bem). Não é muito robusto e assume a entrada exatamente como especificado na pergunta.

Howard
fonte
Na quinta linha, você poderia mudar y*0.5para y/2e se livrar de dois caracteres? Não conheço Ruby, então posso não estar certo.
PhiNotPi
2
@PhiNotPi Infelizmente isso não vai funcionar. y/2faz divisão inteira.
Howard
Você pode usar em loop{}vez de ()while 1?
defhlt
Encontrei isso através do link na barra lateral do lado direito. Isso é muito bem feito. Eu me diverti tentando diminuir esse tamanho, mas encontrei apenas 9 bytes , um byte contando com os literais racionais introduzidos no ruby ​​2.1 (?).
Blotange
5

Python 2: 320 caracteres

N=20
r=range(N+1)
d={}
while(1):
 l=raw_input()
 d[l[1]]=l[6:].replace('^','**')
 g=[[' ']*(N+1) for i in r]
 for n,f in d.items():
  for x in r:
   v=N/2+int(round(2*eval(f.replace('x','(%f)'%(x/2.0-N/4)))))
   if 0<=v<=N:g[N-v][x]=n
 for i in r:
  g[i][N/2]='+|'[i%2]
  g[N/2][i]='+-'[i%2]
 for l in g:print''.join(l)

Provavelmente poderia ser mais curto, mas sou um pouco novato nisso :)

Fazer Numa variável desperdiça 9 caracteres, mas eu gosto mais dessa maneira.

NicolasP
fonte