Um sistema numérico simples

19

Deixe-me falar sobre um sistema numérico simples. (que eu inventei apenas para esse desafio)

Este sistema contém as funções (), [], {}e <>.

1 ()

Quando ()não há argumentos, ele avalia como 0.

Quando ()é dado um ou mais argumentos, ele avalia a soma dos argumentos.

2) []

Quando []não há argumentos, ele avalia como -1.

Quando []é fornecido um ou mais argumentos, ele avalia o primeiro argumento menos a soma dos outros argumentos.

3) {}

Quando {}não há argumentos, ele avalia como 1.

Quando {}recebe um ou mais argumentos, ele avalia o produto desses argumentos.

4) <>

Quando <>não há argumentos, ele avalia como 1.

Quando <>recebe um ou mais argumentos, ele avalia o primeiro número inteiro dividido pelo produto dos outros argumentos.

Sua tarefa

Dada uma sequência que contém um número válido (isso significa que os colchetes são balanceados, sem divisão por 0s etc.) nesse sistema simples de números, imprima seu valor.

Casos de teste

() -> 0
(()()) -> 0
([][]) -> -2
({}<>) -> 2
({}[]) -> 0
[] -> -1
[[][]] -> 0
[()<>] -> -1
{()} -> 0
{([]<>)} -> 0

Lembre-se, isso é , então o código com o menor número de bytes vence.

Oliver Ni
fonte
13
Eu tenho um ótimo nome para isso, que acabei de inventar e não consegui de nenhum outro lugar: quebra-cabeças!
ETHproductions
4
@ETHproductions no
Oliver Ni
Tipo de parentes
DJMcMayhem
2
Divisão é flutuação ou divisão inteira?
Xnor
11
@ Oliver Como a divisão de números inteiros funciona quando um ou ambos os operandos são negativos? Quais são os 4 resultados esperados para 5/3, 5/-3, -5/3e -5/-3?
Martin Ender

Respostas:

2

Dyalog APL , 94 bytes

o←{(⊂(1⊃⍵),⍺⍺⊃⍵),2↓⍵}⋄u←{(⊃⍵,⍺⍺1)⍺⍺⍵⍵/1↓⍵}⋄⍎⍕'+/o' '-u+o' '×/o' '÷u×o' '(⊂⍬),'[')]}>'⍳⌽⍞],'⊂⍬'

usa ⎕IO←0

substitui )]}>por uma chamada de função que pega uma pilha, aplica uma operação em seu quadro superior, exclui-o e anexa o resultado ao seu próximo quadro (o operador monádico oé usado para isso; o operador diádico ulida com os casos mais complicados de -e ÷)

substitui ([{<pelo código que adiciona um quadro à pilha ( (⊂⍬),)

executa a expressão resultante (invertida, para corresponder à ordem de execução da APL) com uma pilha inicial de um quadro vazio ( ⊂⍬)

ngn
fonte
5

Haskell, 357 306 277 251 228 224 188 185 180 bytes

Um analisador baseado em token com uma pilha explícita. (%)pega uma pilha de tokens e um caractere e pressiona (código de operação, número padrão) ou (0, número) para ({[<, ou exibe os números mais altos e um código de operação e pressiona a resposta )}]>. Os códigos de operação são codificados por um corte de enumeração ascii.

Parabéns a @ChristianSievers por sua ótima resposta, da qual pedi emprestadas algumas idéias.

One-liner!

s%c|elem c"([<{",g<-div(fromEnum c)25=(g,[0,0,1,-1,1]!!g):s|(a,(o,n):b)<-span((==0).fst)s=(0,[foldr1(flip$[(+),quot,(-),(*)]!!(o-1))$snd<$>a,n]!!(0^length a)):b
snd.head.foldl(%)[]

Agora com menos manipulação de erros! Uso:

*Main> map (snd.head.foldl(%)[]) ["()","(()())","([][])","({}<>)","({}[])","[]","[[][]]","[()<>]","{()}","{([]<>)}"]
[0,0,-2,2,0,-1,0,-1,0,0]

Obrigado @ChristianSievers por salvar 14 + 3 bytes!

Obrigado @Zgarb por salvar alguns + 4 bytes!

Angs
fonte
11
Que tal (0,[0,0,1,-1,1]!!o):sna quinta linha?
Christian Sievers
@ChristianSievers, é claro!
Angs
Mude as definições de !, para que você possa fazer (s:_)!_=d so segundo caso. Além disso, acho que você poderia ligar x<-p$d<$>init a,y<-d$last ano último caso de %.
Zgarb
@Zgarb thanks! Encontrei uma maneira de unificar ainda mais a avaliação.
Angs
11
Na terceira linha %você pode soltar parênteses ao redor _:be g c.
Zgarb
3

Python 2, 292 265 248 235 223 206 204 bytes

r=reduce
s=input()
for n in')]}>':s=s.replace(n,'),')
for a in'(*x:sum(x)','[a=-1,*x:a-sum(x)','{*x:r(int.__mul__,x,1)','<a=1,*x:r(int.__div__,x,a)':s=s.replace(a[0],'(lambda %s)('%a[1:])
print eval(s)[0]

Substitui todos os colchetes por um lambda que faz o que o colchete faz e avalia o código Python resultante. Requer sua entrada entre aspas, como '[<><>([]{})]'.

Este programa armazena o tipo de colchete como o primeiro caractere em cada seqüência de caracteres no for, e tudo depois da palavra-chave lambdacomo o restante. Ele então usa o primeiro caractere para substituir; o resto é combinado em um lambda (lambda*x:sum(x))().

Experimente no Ideone!

Cobre
fonte
3

PEG.js (ES6) , 132 bytes

x=a:[([{<]b:x*[)\]}>]{var x='([<'.indexOf(a)
b.length^1||b.push(0)
return~~eval(b.length?b.join(('+-/'[x]||'*')+' '):~-('10'[x]||2))}

Deve ser corrigido agora.

Explicação

Mais legível:

x=a:[([{<]
  b:x*
  [)\]}>]
{
  var x='([<'.indexOf(a)
  b.length^1||b.push(0)
  return ~~eval(
    b.length?
      b.join(('+-/'[x]||'*')+' ')
    :~-('10'[x]||2))
  )
}

O PEG.js é uma versão estendida do Javascript criada especificamente para análise. É MUITO rigoroso, e é por isso que eu tive que usar var. Além disso, parece haver um bug com colchetes dentro de strings, que incharam significativamente o código.

Para começar, definimos uma regra xque corresponde a qualquer colchete aque pode ou não conter várias expressões que correspondem à regrax .

Para cada partida para regra x, pressionamos um 0 na matriz da partida interna bseb o comprimento for 1.

Se bo comprimento for maior que 0, então encontramos o índice de ain ([<e obtemos um caractere +-/usando esse índice. Se o resultado for indefinido (o que significava que aera {), transformamos o resultado em *. Finalmente, aderimos a um espaço e nos juntamosb ao resultado.

Se bcomprimento = 0, então encontramos o índice de ain ([<e obtemos um caractere 10usando esse índice. Se o resultado é indefinido (significando que aera {ou <), transformamos o resultado em 2. Finalmente, simplesmente diminuímos.

No final, podemos apenas avaliar a expressão e somar o resultado.

Mama Fun Roll
fonte
3

Perl, 113 + 2 = 115 bytes

Corra com -lp(penalidade de 2 bytes).

/\W/,eval"sub $`\{\$#_?(shift)$&&$'1}"for qw'a+a:1- b-a:- c*c: d/c:';y/([{</a-d/;s/\W/0),/g;s/\pL\K/(/g;$_=eval

Mais legível (nota: esta "versão mais legível" não será executada, porque eu coloquei comentários em locais que não são permitidos sintaticamente):

              # -p option: read a line of input into $_ at program start
              # -l option: remove the final newline whenever reading
do {          # for each element of a list, given later:
  /\W/;       # place an initial identifier in $`, the next character in
              # $&, and the rest of the element in $'
  eval qq{    # then evaluate the following template, with substitutions:
    sub $` {  # define a subroutine named $`, that does this:
      \$#_ ?  # if there is more than one argument                   
      (shift) # then return the first argument $&-ed with
      $& &$'  # the result of a recursive call with the tail of the arguments
              # else (the "else" is a colon taken from $', not the template)
      1       # return (the remainder of $' applied to) 1
    }
  }
} for qw'     # specify the list by splitting the following on whitespace:        
  a+a:1-      # a(head,tail) = len(tail>1) ? head+a(tail) : 1-1
  b-a:-       # b(head,tail) = len(tail>1) ? head-a(tail) : -1
  c*c:        # c(head,tail) = len(tail>1) ? head*c(tail) : 1
  d/c:        # d(head,tail) = len(tail>1) ? head/c(tail) : 1
';
y/([{</a-d/;  # replace ( [ { < with a b c d in $_
s/\W/0),/g;   # replace whitespace, punctuation in $_ with the string "0),"
s/\pL\K/(/g;  # place a ( after (\K) each letter (\pL) in $_
$_=eval       # evaluate $_ as a Perl program, storing the result back in $_
              # -p option: print $_ to the user at program end
              # -l option: output a newline whenever printing

A idéia básica é que estamos convertendo uma entrada como [()<>]no programa Perl b(a(0),d(0),0),via processamento de texto; Perl está bem com a vírgula à direita. No início, definimos as funções a, b, c, dpara ter o mesmo efeito que o (), [], {}, <>construções de linguagem que está implementando; cada um desconsidera seu último argumento (o 0 no final), que é incluído para garantir que todas as entradas sejam analisadas corretamente e funcionem usando a implementação comumente vista na programação funcional em que a cabeça e a cauda são processadas separadamente. Porque b(e,f,g,0)significa e-f-g, isto é, trata especialmente seu primeiro argumento, enquanto atrata seus argumentos simetricamente (a(e,f,g,0) meios e+f+g), implementamosarecursivamente e bvia chamada a. ce dtem um relacionamento semelhante. Todas essas quatro funções são muito semelhantes, portanto, nós as geramos em tempo de execução, em vez de implementá-las separadamente; armazenamos um modelo que se aplica a todas as quatro funções em uma string e, em seguida, geramos as funções substituindo caracteres no modelo.

Como o Perl /faz a divisão de ponto flutuante, o implementado {}também. Suponho que isso não seja um problema por si só ou -Minteger(selecione uma variante de idioma em que todas as operações aritméticas sejam operações inteiras) seja gratuito, porque, caso contrário, eu teria que gastar bytes extras escrevendo uma divisão inteira em Perl, que não parece ser o principal problema do problema. (Eu acho que você precisaria gastar quatro bytes mudando (shift)para int+(shift); eu não testei isso.)


fonte
2

Octave, 215 206 198 bytes

S='sum(x(:))-sum(x(2:end))';Y='(@(x)isempty(x)';c=']) ';[~,~,u]=unique(['()<>[]{}',input('')]);eval([{'sum([',c,[Y,'+fix((',S,')/prod(x(2:end))))(['],c,[Y,'*-1+',S,'*2)(['],c,'prod([',c}{u(9:end)}])

experimente online

rahnema1
fonte
2

PHP, 315 300 285 258 250 244 bytes

for($s=$argv[1];$r=!$r;)foreach(["(+)1","[-]0","{*}2","</>2]as$p)if(preg_match("#$e$p[0]([-_\d]*)$e$p[2]#",$s,$m)){if(""==$v=strtok($m[1],_))$v=$p[3]-1;while(""<$n=strtok(_))eval("\$v$p[1]=$n;");$s=strtr($s,[$m[$r=0]=>_.$v]);}echo substr($s,1);

substitui subexpressões por sublinhado + valor; o loop quebra quando a iteração não substitui.

19 anos desde que conheci C, 17 anos trabalhando com PHP;
esta é a primeira vez que strtokfaz sentido ... ajudando a economizar 24 bytes!

demolir

for($s=$argv[1];    // take input from argument
    $r=!$r;)        // toggle $r; loop until no replacement has taken place
    foreach(["(+)1","[-]0","{*}2","</>2]as$p) // loop through operations
        if(preg_match("#$e$p[0]([-_\d]*)$e$p[2]#",$s,$m))   // find a match
        {
            if(""==$v=strtok($m[1],_))  // init $v with first token from sub-match
                $v=$p[3]-1;             // if no token, init with default value
            while(""<$n=strtok(_))      // loop through tokens
                eval("\$v$p[1]=$n;");       // operate
            $s=strtr($s,[$m[$r=0]=>_.$v]);  // reset $r; replace match with underscore+value
        }
echo substr($s,1);  // print result
Titus
fonte
@ Oliver não bate em ninguém aqui; mas obrigado pela diversão!
Titus
2

ES6 (Javascript), 250, 171, 154, 149, 147 bytes

Uma versão Javascript pura.

A "metaprogramação" (como a maioria das outras respostas aqui) converte o texto do programa de entrada em um programa Javascript correspondente, aplicando várias substituições diretas de texto (ou seja, mantendo a estrutura do programa como está).

Provavelmente pode ser jogado mais.

ATUALIZAÇÃO (v2.1)

  • Menos dois bytes (parênteses removidos na expressão ternária)
  • Golpeou mais 5 bytes, usando variável para extração de resultados e se livrando de "[]" extra

ATUALIZAÇÃO (v2)

Acabei de perceber que vírgulas pendentes nas matrizes ES são totalmente válidas, portanto todo o código de normalização de vírgula pode ser removido. Também seguiu um excelente conselho de @Titus, sobre a otimização da pesquisa do alfabeto.

ATUALIZAÇÃO (v1)

Removido o alias duplicado de "substituição".

ATUALIZAÇÃO (v1)

  • Use um alfabeto melhor: () => 1+ [] => 0 {} => 2 * <> => 2 / (cada caractere pode ser reutilizado diretamente como valor ou operador)

  • Substituir reduzir () por substituir () (mapeamento do alfabeto)

  • Processamento constante de braçadeiras de embutimento, abertura e fechamento constantes em uma única etapa

Golfe (v2.1)

s=>eval("o="+s.replace(/./g,r=>"2+1-3*3/"["()[]{}<>".indexOf(r)]).replace(/\d\D?|\D/g,r=>r[1]?r[0]-2+",":r*1?'([':`].reduce((r,a)=>r${r}a)),`)+"o

Golfe (v1)

(s,A="(2)+[1]-{3}*<3>/")=>eval(s[R="replace"](/./g,r=>A[A.indexOf(r)+1])[R](/\d\D?|\D/g,r=>r[1]?r[0]-2+",":(r[0]*1?'([':`].reduce((r,a)=>r${r}a)),`))[R](/,(\])|,$/g,"$1"))    

Golfe (v0)

([...s],A="(a)b[c]d{e}f<g>h",R="replace")=>eval(s.reduce((r,c)=>r+=A[A.indexOf(c)+1],'')[R](/ab|cd|ef|gh/g,r=>({d:-1,b:'0'}[r[1]]||1) + ',')[R](/[aceg]/g,"([")[R](/[bdfh]/g,r=>`].reduce((r,a)=>r${"+*-/"["bfdh".indexOf(r)]}a)),`)[R](/,(\])|,$/g,"$1"))

Explicado (v0)

//BEGIN 

//s - input text, A - alphabet, R - "String.replace()" alias
E=([...s],A="(a)b[c]d{e}f<g>h",R="replace")=>eval(

//Replace input alphabet by a more friendly one, to avoid too much escaping and quoting
// () - ab, [] -cd, {} - ef, <> - gh
s.reduce((r,c)=>r+=A[A.indexOf(c)+1],'')

//Replace no-arg invocations with a corresponding constant value
// () => 0, [] => -1, {} => 1, <> => 1      
[R](/ab|cd|ef|gh/g,r=>({d:-1,b:'0'}[r[1]]||1) + ',')

//Replace opening brackets with "(["
[R](/[aceg]/g,"([")

//Replace closing brackets with "].reduce(...)),"
//An arithmetic operation to apply (+-*/) is chosen based on the bracket type 
//and is substituted into the template 
[R](/[bdfh]/g,r=>`].reduce((r,a)=>r${"+*-/"["bfdh".indexOf(r)]}a)),`)

//Strip excessive commas
[R](/,(\])|,$/g,"$1")
);

//END: eval() the result


Example:
E("{([]<>()<>{})(<><>)}")
=> eval("([([-1,1,0,1,1].reduce((r,a)=>r+a)),([1,1].reduce((r,a)=>r+a))].reduce((r,a)=>r*a))")
=> 4

Teste

E=([...s],A="(a)b[c]d{e}f<g>h",R="replace")=>eval(s.reduce((r,c)=>r+=A[A.indexOf(c)+1],'')[R](/ab|cd|ef|gh/g,r=>({d:-1,b:'0'}[r[1]]||1) + ',')[R](/[aceg]/g,"([")[R](/[bdfh]/g,r=>`].reduce((r,a)=>r${"+*-/"["bfdh".indexOf(r)]}a)),`)[R](/,(\])|,$/g,"$1"))

T=(s,a)=>{
    console.log(s,r=E(s),r==a?"OK":"NOT OK");
}

T("()",0)
T("(()())",0) 
T("([][])",-2)
T("({}<>)",2) 
T("({}[])",0) 
T("[]",-1)
T("[[][]]",0) 
T("[()<>]",-1) 
T("{()}",0) 
T("{([]<>)}",0)

Saída de teste

() 0 OK
(()()) 0 OK
([][]) -2 OK
({}<>) 2 OK
({}[]) 0 OK
[] -1 OK
[[][]] 0 OK
[()<>] -1 OK
{()} 0 OK
{([]<>)} 0 OK
zepelim
fonte
11
Sua versão v0 poderia ser com s.reduce((r,c)=>r+="abcdefgh"["()[]{}<>".indexOf(c)],'')(-5)? Nesse caso, você pode se lembrar indexOfde uma variável e pegar o operador de uma terceira string literal.
Titus
2

Haskell, 184 179 172 161 161 160 159 151 148 145 bytes

s%(c:i)|elem c")}]>"=([1?(*),sum,1?quot,(-1)?(-)]!!mod(fromEnum c)5$s,i)|(r,j)<-[]%i=(s++[r])%j
[v]%e=(v,e)
(v?_)[]=v
(_?o)s=foldl1 o s
fst.([]%)

Descida recursiva, enfiando a entrada porque Haskell. Como de costume, a última linha não é uma definição, mas afirma a função que resolve o problema. Portanto, para testar, coloque as linhas, exceto a última em um arquivo, carregue-a e faça algo assim:

*Main> fst.([]%) $ "{([][][])([][])}"
6

Agradecemos a @Zgarb pela inspiração e muitas dicas detalhadas, e a @Angs pela inspiração de sua solução e outras dicas.

Não foi especificado como a divisão deve se comportar com números inteiros negativos. De qualquer forma, o uso repetido divparece errado, pois não é o mesmo que usar divuma vez com o produto dos valores restantes. Agora usando quot, eu obtenho os mesmos resultados para<{}([][])[]> e <{}{([][])[]}>.

Para um código agradável, quase legível, veja a primeira versão. As versões intermediárias contêm todos os tipos de código agradável e intimidador e ajudam a entender esta versão.

Peneiradores cristãos
fonte
Eu acho que você pode salvar alguns bytes definindo (!)=(,)e usando em !vez de tuplas explícitas.
Zgarb
Se você definir m xe d xcomo 1#xe 0#x, você pode mesclar os casos m[x]e d[x]em um, que eu acho que poupa alguns bytes também.
Zgarb
@Zgarb Thanks! Quase perdi o último par, sem o qual o !truque não compensa. Sua segunda sugestão é má, lá está o meu código quase legível ... Inteligente!
Christian Sievers
Acabei de perceber que definir s%(c:i)=(s?c,i)e s?')'=sum setc será muito mais curto, pois você pode se livrar dos is repetidos . ..Não, espere, isso provavelmente não vai funcionar por causa do s%(_:i)caso.
Zgarb
11
Perca os backticks eleme div, isso deve economizar mais alguns bytes.
Zgarb 23/11
1

JavaScript (ES6), não eval, 156 bytes

f=s=>s==(s=s.replace(/(.) ?([- \d]*)[\]})>]/,(_,c,s)=>` `+(s?s.split` `.reduce((l,r)=>c<`<`?l- -r:c<`[`?l/r|0:c<`{`?l-r:l*r):c==`[`?-1:c==`(`?0:1)))?+s:f(s)

Explicação: O regexp localiza o primeiro colchete de fechamento não processado e corresponde ao colchete de abertura (presumivelmente) correspondente e a quaisquer argumentos intermediários. Os argumentos são divididos e reduzidos de acordo com a operação (infelizmente '(' e '[' não são o par ideal para +e -)) ou, se não houver argumentos, o valor apropriado é calculado (novamente a correspondência de caracteres com valores é abaixo do ideal) do meu ponto de vista) .O resultado é então substituído por um espaço à esquerda para separá-lo de outros resultados. A função se chama recursivamente até que não haja mais alterações a serem feitas, nesse caso, ele retorna o valor resultante. Exemplo:

[()<>]
[ 0<>]
[ 0 1]
 -1
Neil
fonte
f ("([] [])") => 0 (em vez de 2)
zeppelin
Alguns outros testes também estão falhando para mim (você pode tentar o código de teste na minha resposta ), provavelmente devido a: f ("[]") => 0, pois "[]" faz parte de todos os testes com falha.
Zeppelin #
@ zeppelin Opa, isso foi devido a algum mau golfe. Eu voltei para uma versão anterior, mas infelizmente isso me custa alguns bytes.
30516 Neil