Valuta un'espressione di operatori ternari


29

Prendere in considerazione una grammatica sopra l'alfabeto { 0, 1, ?, :} definita dalla regola di produzione

s → 010 ?s :s ┃ 1 ?s :s

Data una stringa generata da s , analizzala come un'espressione in cui ?:è associativo a destra (ad esempio, a?B?X:Y:c?d:e?f:gsignifica a?(B?X:Y):(c?d:(e?f:g))) e valutala con la seguente semantica:

eval(0) = 0
eval(1) = 1
eval(0?a:b) = eval(b)
eval(1?a:b) = eval(a)

Se il risultato è 0 , genera un valore fisso; se l'uscita è 1 , emette un valore fisso diverso. Specificare i valori di output scelti (ad es. 0/ 1, O False/ True) nella risposta.

Casi test

0 -> 0
1 -> 1
0?0:1 -> 1
0?1:0 -> 0
1?0:1 -> 0
1?1:0 -> 1
0?1?0:1:1 -> 1
1?0?1:1:1 -> 1
1?0:1?0:1?1:1 -> 0
1?1?1:0?1?0:0:0:0 -> 1
1?0:1?0?1:1?1:0:1?1?1:1:1?0:1 -> 0
1?1?1:0?0?1:1:0?1:0:1?1?0?0:0:1?1:0:0?1?0:1:1?0:1 -> 1
0?0?1?0?0:1:0?0:0:0?0?1:1:1?0:1:0?0?0?1:0:0?1:1:1?1?0:1:1 -> 0

Regole

  • Non è possibile utilizzare il linguaggio integrato che interpreta le stringhe come codice in alcuni linguaggi di programmazione ed eseguirlo (come JavaScript / Perl / Ruby / Python eval).
  • Detto questo, il codice non deve effettivamente analizzare e quindi valutare la stringa di input. Puoi seguire qualsiasi approccio per ottenere risultati equivalenti e non violare la regola precedente.
  • Il tuo programma verrà verificato rispetto perl -le 'print eval<>'.
  • Vince il codice più breve (in byte).

Che ne dici di usare il linguaggio integrato come eval che interpreta le stringhe come codice $ my_language dopo aver cambiato radicalmente la stringa?
Adám,

Che dire dei builtin che interpretano le stringhe come codice $ some_other_language ?
Mego

@ Adám Sarebbe vietato, mi dispiace.
Lynn,

@Mego Hmm, c'è una banale opportunità di barare lì, quindi ho esteso la regola per coprire tutti questi built-in.
Lynn,

1
Alla luce di casi di test di Martin, forse sarebbe più semplice per definire la grammatica S → T | T ? S : S, T → 0 | 1, eliminando la necessità di parlare di associatività?
Peter Taylor,

Risposte:



17

Retina , 23 byte

r-1=+`0\?.:|1\?(.):.
$1

Provalo online! (La prima riga abilita una suite di test separata da avanzamento riga.)

Spiegazione

In realtà è abbastanza semplice. L'input viene ridotto al risultato +valutando ripetutamente ( ) ternari che contengono solo valori letterali. Per assicurarci che ciò avvenga in modo associativo, cerchiamo le corrispondenze da destra a sinistra ( r) e sostituiamo solo l'ultima corrispondenza che troviamo ( -1=).

Il regex stesso corrisponde 0\?.:e lo rimuove (lasciando solo il materiale dopo :) o 1\?.:.lo sostituisce con il valore dopo il ?.


Se la regex inizia da destra, allora non dovresti elaborare la 1corrispondenza della st invece della -1st?
Leaky Nun,

@LeakyNun Purtroppo, penso di invertire le partite prima di applicare il limite.
Martin Ender,

10

Haskell, 106 101 100 90 83 byte

Ciò si basa fortemente sulle capacità di corrispondenza dei modelli di Haskell. Prima di tutto, invertiamo la stringa in modo tale da poter solo cercare la prima occorrenza di b:a?x(che normalmente si legge come x?a:b) e sostituirla con il suo valore. Ciò fornisce automaticamente la giusta associatività . Qui usiamo il x:xsmodello. Questo è ciò che la funzione fsta facendo. Quindi, in pratica, applichiamo fripetutamente l'output, fino a quando rimane un solo numero (0 o 1).

Grazie @Lynn per 12 byte!

f(b:':':a:'?':x:l)|x>'0'=a:l|1>0=b:l
f(x:l)=x:f l
f x=x
until((<2).length)f.reverse

8

Brainfuck, 82 64 63 byte

+
[
  ,>>,>
  +++++++[<---<<-[------>>]<-]
  <<
  [
    ->[<++>[+]]
  ]
  +>[>>]
  <<<-
]
<.

L'output è \xffper 0e \x00per 1. L'implementazione del brainfuck deve consentire di andare a sinistra della cella iniziale.

Questo utilizza essenzialmente lo stesso approccio della risposta Python di xsot , ma la ramificazione è probabilmente più difficile da seguire rispetto alla mia presentazione iniziale a 82 byte:

-
[
  +
  [
    ->,,>+++++++[<--------->-]
    <[<++>[+]]
    <
  ]
  ,->,>+++++++[<[-------<]>>-->-]
  <[<]
  <
]
>>.

(Per questa soluzione, l'output è \xfeper 0e \xffper 1e una maggiore compatibilità si ottiene quando l'input termina con una nuova riga.)

Se non si può essere disturbati ad analizzare la soluzione di xsot, l'idea è questa: procedere da sinistra a destra. Se vedi, 1?scartalo avidamente. Se vedi, 0?scarta tutto tra quello e il corrispondente :. Quando ?non viene visualizzato come secondo carattere, interrompere il ciclo e stampare il primo carattere della stringa rimanente.

Quindi, la soluzione a 82 byte rispecchia in realtà questo schema abbastanza da vicino. Il ciclo interno si gestisce 0?, proprio come il ciclo interno di xsot. È necessario prestare attenzione per accedere al ciclo principale senza controllare alcun carattere di input; cioè, vogliamo verificare se il secondo carattere è ?solo una volta alla fine del loop principale e non anche all'inizio prima di entrare nel loop principale.

La soluzione a 63 byte fondamentalmente combina i loop interni ed esterni in uno, che sospettavo fosse possibile data la somiglianza tra questi loop. Il layout di memoria nel loop principale potrebbe essere descritto come:

[s] d c

dove [x]significa cella corrente - sinizia come un valore fittizio diverso da zero che indica solo che stiamo ancora eseguendo il loop e viene immediatamente sovrascritto con un carattere di input (o 0o 1). La dcella mantiene la profondità (negativa) nel caso in cui ci troviamo nel mezzo di una 0?, altrimenti 0. La csta per essere sia ?o :fine riga o EOF.

Dopo l'aggiornamento se c, gestiamo il 0?caso aggiornando di dconseguenza e regolando il puntatore, altrimenti utilizziamo il valore corrente di ccome valore di dnella successiva iterazione, o interrompiamo se abbiamo finito.


7

Python 2, 76 74 73 72 byte

a=`id`
for c in input()[::-1]:a=(c+a,a[ord(c)*7%9]+a[4:])[a>'?']
print a

Con input come stringa letterale da evitare raw_.

L'output è 0o 1seguito da <built-in function id>.


1
Haha, ho appena letto la tua risposta per bang e stavo per pubblicare una risposta quasi identica! Ecco un'ulteriore ottimizzazione:3>>int(c)
xsot

Vuoi spiegare come funziona? Sembra davvero pulito
WorldSEnder

@WorldSEnder Penso che sia il tipo di soluzione che può essere difficile da trovare, ma facile da capire una volta che lo vedi. Attraversa la stringa all'indietro ed elabora ripetutamente il condizionale più a destra, come hanno fatto anche altri solutori.
Mitch Schwartz,

Quel `id`trucco ...! Ben fatto :)
Lynn,

5

Python 2, 89 byte

s=input()
while'?'<=s[1:]:
 n=s<'1'
 while n:s=s[2:];n+=-(s[1]<'?')|1
 s=s[2:]
print s[0]

L'input è preso come una stringa letterale.


5

Grime , 34 31 byte

E=d|d\?E.E
e`\1|\1\?_.E|\0\?E._

Stampe 1per input veritieri e 0per input falsi. Provalo online! Purtroppo l'ultimo test case esaurisce la memoria su TIO.

Spiegazione

L'associatività giusta significa essenzialmente che in a?b:c, aè sempre o 0o 1, mai un'espressione più lunga. Definirò in modo ricorsivo uno schema che corrisponda a un'espressione sincera del genere e verificherò l'input rispetto ad esso. E 'anche necessario controllare che ogni :è davvero una :, se le ?s sono tutti controllati: c'è un numero uguale di ?s e :s nell'input, e se alcuni ?è classificato in modo non corretto come :, il corrispondente :sarà in grado di soddisfare, e la congruenza di Grime il motore tornerà indietro.

E=d|d\?E.E
E=                      Define nonterminal E (for "expression") as
  d|                     a digit, OR
    d                    a digit,
     \?                  a literal ?,
       E                 a match of E,
        .                any character (will match a :), and
         E               another match of E.
e`\1|\1\?_.E|\0\?E._
e`                      Match entire input against this pattern (truthy expression):
  \1|                    a literal 1, OR
     \1\?                a literal 1?,
         _               a recursive match of truthy expression,
          .              any character (will match a :), and
           E|            any expression, OR
             \0\?E._     the same, but with 0 in front, and _ and E swapped.

5

Haskell, 79 71 70 62 60 56 byte

Modifica: grazie a @Zgarb per 3 byte e @nimi per 4 byte!

e(x:'?':r)|a:_:s<-e r=last$e s:[a:tail(e s)|x>'0']
e x=x

Questo è un approccio ricorsivo che abusa in qualche modo della regola dell'output "del valore fisso" . Modifica: sbarazzarsi delle tuple non solo salva 8 byte, ma produce anche un output migliore: "0"o "1".

Versione non golfata:

eval (x:'?':r1) = if x=='1' then (a, r3) else (b, r3)
    where (a,':':r2) = eval r1
          (b, r3)    = eval r2
eval (x:r) = (x,r)

Come funziona?
La evalfunzione attraversa l'albero implicito delle espressioni

eval 1?0?0:1:0?1:0 -> eval 1?          :
                             eval 0?0:1 eval 0?1:0

e restituisce una tupla del modulo (result, rest).
Il primo modello (x:'?':r1)corrisponde xa '1'e r1a "0?0:1:0?1:0". Applicare ricorsivamente evalper r1valutare la sottoespressione 0?0:1e i ritorni (0,":0?1:0"). Abbinando questo ai (a,':':r2)rendimenti del modello a=0e r2=0?1:0. Questa sotto-formula viene anche valutata ricorsivamente in modo tale che b='0'e r3="". Controlla se xè '1'o '0'e restituisci uno (a, r3)o (b, r3).


1
Bel approccio! Funzionerebbe x>'0'al posto di x=='1'?
Zgarb,

Grazie, non ci ho pensato mentre mi occupavo dei caratteri.
Laikoni,

1
Io credo che si può anche sostituire ':'con _.
Zgarb,

Sì, quindi il codice funziona anche con delimitatori arbitrari anziché solo :. Grazie ancora!
Laikoni,

1
Bello! È possibile sostituire il if .. then .. elsecon last$e s:[a:tail(e s)|x>'0'].
nimi,

3

JavaScript (ES6), 53 byte

f=s=>s[1]?f(s.replace(/0\?.:|1\?(.):.(?!\?)/,"$1")):s

Restituisce 0o 1per input valido; si blocca per input non valido. Spiegazione: poiché l'inversione di una stringa è scomoda in JavaScript, il mio primo tentativo di 71 byte ha funzionato utilizzando un lookahead negativo per un ?che altrimenti disturberebbe l'associatività:

f=s=>s[1]?f(s.replace(/(.)\?(.):(.)(?!\?)/,(_,a,b,c)=>+a?b:c)):s

Dato che questo era un po 'lungo, mi chiedevo se potevo migliorare le cose incorporando il processo decisionale nel regexp. Come si è scoperto, non è stato un successo immediato, in quanto ha impiegato anche 71 byte:

f=s=>s[1]?f(s.replace(/0\?.:(.)(?!\?)|1\?(.):.(?!\?)/,"$1$2")):s

Poi mi venne in mente che 0?0:e 0?1:sono sempre no-ops, senza preoccupazione per associatività. Questo mi ha permesso di risparmiare quasi il 25%.


Manca il blocco di codice in alto f=. Non ho verificato se il numero di byte lo sta prendendo in considerazione o meno.
Patrick Roberts,

@PatrickRoberts Lo faccio per sempre, perché copio dal registro, che mostra solo il risultato del compito (che è sufficiente per le funzioni non ricorsive ovviamente).
Neil,

@Neil è possibile copiare dall'input del registro anziché dall'output
solo ASCII il

@MarsUltor L'input del registro include il prompt, che devo quindi ricordare di escludere. Questo è un passaggio scomodo in più per le funzioni non ricorsive, motivo per cui copio dall'output di default.
Neil,

3

Perl, 32 + 1 ( -pflag) = 33 byte

Tutto merito a @Mitch Swartch , poiché la sua soluzione era di 14 byte più corta della mia!
Grazie anche a @Neil che ha suggerito una soluzione 1 byte più lunga di Mitch.

s/.*\K(0\?..|1\?(.)..)/\2/&&redo

Ha bisogno di -pbandiera, così come -M5.010o -Eper funzionare. Per esempio :

perl -pE 's/.*\K(0\?..|1\?(.)..)/\2/&&redo' <<< "0
0?0:1
0?1?0:1:1
1?0:1?0?1:1?1:0:1?1?1:1:1?0:1
0?0?1?0?0:1:0?0:0:0?0?1:1:1?0:1:0?0?0?1:0:0?1:1:1?1?0:1:1"

Spiegazioni : Riduce sostanzialmente i blocchi di a?b:c(a partire dalla fine per essere sicuri che non ?segua) in bo in cbase alla veridicità di a, ripetutamente fino a quando la stringa contiene solo 1o 0.


Non -conta per il tuo punteggio? Hrm. Interessante ... Buona risposta!
MayorMonty,

@MayorMonty Per un 1-liner è possibile invocarlo dalla riga di comando utilizzando perl -e '<code>'quindi l'aggiunta di un psolo costo di 1 byte perl -pe '<code>'.
Neil,

@Neil Ahh, questo ha senso
MayorMonty,

In realtà non devi invertire la stringa, puoi solo un lookahead negativo per a ?, sono stato in grado di ridurlo a 34 byte in questo modo.
Neil,

Ecco un 32 + 1:s/.*\K(1\?(.)..|0\?..)/\2/&&redo
Mitch Schwartz,

2

Python 3, 93 69 byte

def f(s):z=s.pop;r=z(0);return s and':'<z(0)and(f(s),f(s))[r<'1']or r

L'input è la stringa come un elenco di caratteri, l'output è "0"o"1"

>>>f(list("0?0:1"))
<<<"1"

Versione non golfata:

def parse(s):
    predicate = s.pop(0)
    if s and s.pop(0) == '?':
        left, right = parse(s), parse(s)
        if predicate == '0':
            return right
        return left
    return predicate

Un altro tentativo, ma con molti più byte:

i=input()[::-1]
a=[i[0]]
for o,z in zip(i[1::2],i[2::2]):a+=[z]if o<'?' else[[a.pop(),a.pop()][z>'0']]
print(a[0])

La tua risposta potrebbe essere una funzione: puoi rimuovere la seconda riga.
Lynn,

Questo è chiaramente non testato, in quanto non supera i casi di test e la versione non controllata dà un errore di runtime. La tua idea di base è buona però. Con alcune modifiche ottengo 68 in Python 2 e 69 in Python 3.
Mitch Schwartz

1
Beh, penso che abbia più senso darti la risposta che modificarla nella mia (dal momento che non stavo pensando a questo tipo di approccio prima di vedere la tua risposta) o restare in attesa mentre la tua risposta è buggy. Ecco il 68 che ho citato def f(s):x=s.pop(0);return[]<s<s.pop(0)>'>'and(f(s),f(s))[x<'1']or x, e per Python 3 con una piccola distanza di modifica dalla tua, c'è def f(s):z=s.pop;r=z(0);return s and':'<z(0)and(f(s),f(s))[r<'1']or r.
Mitch Schwartz,

Grazie @MitchSchwartz, analizza praticamente da destra, anziché da sinistra, gotcha
WorldSEnder,

1
Altrimenti, a sinistra anziché a destra, ~~~
WorldSEnder,

1

SED, 75 74 68 (40 + 1 per -r) 41

:
s,(.*)1\?(.):.,\1\2,
s,(.*)0\?.:,\1,
t

Probabilmente puoi ridurlo usando il trucco di @ MitchSchwartz nel suo commento , anche se potresti dover usare (.*)e aggiungere un termine di sostituzione aggiuntivo.
Neil,

@Neil, potresti avere ragione, ma non riesco a capire come farlo funzionare.
Riley,

L'ho scritto in chat, poiché la formattazione in un commento potrebbe essere fonte di
Mitch Schwartz

@MitchSchwartz Heh, le etichette vuote funzionano? Ma penso che volevi dire \3invece di \2. Inoltre, puoi unire le linee con ;per ottenere :;s/(.*)(1\?(.):.|0\?.:)/\1\3/;t.
Neil,

@Neil l'ho fatto \3, questo significa che ho copiato accidentalmente una versione precedente. Conosco punti e virgola. Ma schifo, perché dovresti usare il punto e virgola.
Mitch Schwartz,

0

Utilità Bash + GNU, 42

rev|sed -r ':
s/(.):.\?0|.:(.)\?1/\1\2/
t'

Idea simile alla maggior parte delle altre risposte di corrispondenza dei modelli.

Ideone .


Non è necessario acquisire il primo carattere nel 0caso, che consente di risparmiare 5 byte.
Neil,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.