Parse a Quaternion


27

Se non lo sai già, un quaternione è sostanzialmente un numero in 4 parti. Ai fini di questa sfida, ha una componente reale e tre componenti immaginarie . I componenti immaginarie sono rappresentati dal suffisso i, j, k. Per esempio, 1-2i+3j-4kè un quaternione con 1essendo la componente reale e -2, 3ed -4essendo le componenti immaginarie.

In questa sfida devi analizzare la forma di stringa di un quaternione (es. "1+2i-3j-4k") In un elenco / matrice di coefficienti (es. [1 2 -3 -4]). Tuttavia, la stringa di quaternione può essere formattata in molti modi diversi ...

  • Potrebbe essere normale: 1+2i-3j-4k
  • Può avere termini mancanti: 1-3k, 2i-4k(Se si dispone di termini mancanti, uscita 0per quei termini)
  • Potrebbe essere mancante coefficienti: i+j-k(In questo caso, questo equivale a 1i+1j-1kIn altre parole, a. i, jO ksenza un numero davanti si assume di avere un 1fronte di default)
  • Potrebbe non essere nel giusto ordine: 2i-1+3k-4j
  • I coefficienti possono essere semplicemente numeri interi o decimali: 7-2.4i+3.75j-4.0k

Ci sono alcune cose da notare durante l'analisi:

  • Ci saranno sempre termini tra +o-
  • Ti verrà sempre inviato un input valido con almeno 1 termine e senza lettere ripetute (no j-j)
  • Tutti i numeri possono essere considerati validi
  • È possibile modificare i numeri in un'altra forma dopo l'analisi, se si desidera (es. 3.0 => 3, 0.4 => .4, 7 => 7.0)

Non sono consentite analisi incorporate / quaternioni e scappatoie standard. Questo include evalparole chiave e funzioni. L'input sarà una singola stringa e l'output sarà un elenco, un array, valori separati da spazi bianchi, ecc.

Dato che si tratta di , vince il codice più corto in byte.

Tonnellate di casi di test

1+2i+3j+4k             => [1 2 3 4]
-1+3i-3j+7k            => [-1 3 -3 7]
-1-4i-9j-2k            => [-1 -4 -9 -2]
17-16i-15j-14k         => [17 -16 -15 -14]

7+2i                   => [7 2 0 0]
2i-6k                  => [0 2 0 -6]
1-5j+2k                => [1 0 -5 2]
3+4i-9k                => [3 4 0 -9]

42i+j-k                => [0 42 1 -1]
6-2i+j-3k              => [6 -2 1 -3]
1+i+j+k                => [1 1 1 1]
-1-i-j-k               => [-1 -1 -1 -1]

16k-20j+2i-7           => [-7 2 -20 16]
i+4k-3j+2              => [2 1 -3 4]
5k-2i+9+3j             => [9 -2 3 5]
5k-2j+3                => [3 0 -2 5]

1.75-1.75i-1.75j-1.75k => [1.75 -1.75 -1.75 -1.75]
2.0j-3k+0.47i-13       => [-13 0.47 2.0 -3] or [-13 .47 2 -3]
5.6-3i                 => [5.6 -3 0 0]
k-7.6i                 => [0 -7.6 0 1]

0                      => [0 0 0 0]
0j+0k                  => [0 0 0 0]
-0j                    => [0 0 0 0] or [0 0 -0 0]
1-0k                   => [1 0 0 0] or [1 0 0 -0]

Ci saranno mai +segni non necessari nell'input? Come +1k:?
FryAmTheEggman,

@FryAmTheEggman No. Gli input non inizieranno mai con a +.
GamrCorps

1
Fa -0parte dell'output legale degli ultimi due esempi?
Isaacg,

1
@isaacg sì, va bene
GamrCorps

1
@LLlAMnYP Hai sollevato un buon punto. Consente di definire la evalrestrizione da assumere in una stringa, interpreta come codice e / o input. Le conversioni non contano ai sensi di questo perché non è possibile passare, ad esempio, la stringa "test"a una funzione di conversione di numeri interi per ricevere un numero intero, ma testsarebbe interpretata come codice in una evalfunzione normale . TLDR: eval: no, digitare conversioni: sì.
GamrCorps

Risposte:


5

Pyth, 48 byte

jm+Wg\-K--e|d0G\+K1+]-I#GJczfT.e*k<b\.zm/#dJ"ijk

Suite di test dimostrativi

Il formato di output è separato da newline. Il codice della suite di test utilizza la separazione dello spazio, per facilitare la lettura, ma è altrimenti lo stesso.

Emette a -0negli ultimi 2 casi, che spero vada bene.

Spiegazione da seguire.


9

Retina, 115

\b[ijk]
1$&
^(?!.*\d([+-]|$))
0+
^(?!.*i)
+0i+
^(?!.*j)
0j+
^(?!.*k)
0k+
O$`[+-]*[\d.]*(\w?)
$1
-
+-
^\+

S`[ijk+]+

Provalo online!

1 byte salvato grazie a @Chris Jester-Young .

Un bug risolto e 6 byte salvati grazie a @Martin Büttner

Abbiamo trovato un paio di bug che coinvolgono alcuni casi limite, aumentando un po 'il conteggio dei byte.

Restituisce i numeri newline separati. Comunque, questa ha una soluzione per lo più elegante che in qualche modo viene rovinata dai casi limite, ma ehi devo usare la modalità di ordinamento, ciò significa che ho usato lo strumento giusto per il lavoro, giusto?

Spiegazione:

Fase per fase, come al solito.

\b[ijk]
1$&

Gli unici caratteri nell'input che possono creare limiti di parole sono -+.. Ciò significa che se troviamo un confine seguito da una lettera, abbiamo un implicito 1che aggiungiamo alla sostituzione. $&è sinonimo di $0.

^(?!.*\d([+-]|$))
0+

Grazie mille a Martin per questo, questo aggiunge l'implicito 0per la parte reale se mancava nell'input. Ci assicuriamo di non poter trovare un numero seguito da un segno più o meno o dalla fine della stringa. Tutti i numeri complessi avranno una lettera dopo di loro.

^(?!.*i)
+0i+

I prossimi 3 livelli sono praticamente tutti uguali, salvo che lettera abbiano un impatto. Tutti cercano di vedere se non riusciamo ad abbinare la lettera e se non riusciamo ad aggiungere un 0termine per essa. L'unico motivo iha un extra +prima di impedire che il valore reale sia illeggibile con il icoefficiente s, gli altri numeri sono tutti separati dalla loro variabile complessa.

O$`[+-]*[\d.]*(\w?)
$1

Ah, la parte divertente. Questo utilizza lo stadio di ordinamento newish, indicato dal Osegno di spunta del separatore prima dell'opzione. Il trucco qui è quello di afferrare l'intero numero seguito facoltativamente da un carattere di parola, che in questo caso corrisponderà solo a uno dei ijk. L'altra opzione utilizzata è quella $che fa sostituire il valore utilizzato per ordinare queste corrispondenze. Qui usiamo solo la lettera opzionale rimasta come valore di ordinamento. Poiché Retina ordina lessicograficamente per impostazione predefinita, i valori sono ordinati come se fossero in un dizionario, il che significa che le partite sono in "", "i", "j", "k"ordine.

-
+-

Questo stadio mette un +segno davanti a tutti i segni meno, questo è necessario se abbiamo un valore negativo per inello stadio diviso, in seguito.

^ \ +

Rimuoviamo il lead +per assicurarci di non avere newline in più.

S`[ijk+]+

Dividi le righe rimanenti sulle corse delle variabili complesse o sul segno più. Questo ci dà un valore per riga.


3

Perl 5, 125 byte

#!perl -p
%n=(h,0,i,0,j,0,k,0);$n{$4//h}=0+"$1@{[$3//$5//1]}"while/([+-]?)(([\d.]+)?([ijk])|([\d.]+))/g;s/.*/@n{qw(h i j k)}/

1
@KennyLau Purtroppo, la modifica proposta non fa ciò che ti aspetti. L'ho provato prima di pubblicare la mia risposta. ;-)
Chris Jester-Young,

@KennyLau Per quanto riguarda questo cambiamento proposto , le \apartite di Perl su "allarme", non alfabetico. C'è \wper il carattere di parola (alfanumerico e trattino basso), ma qui non funzionerà; abbiamo bisogno che non corrisponda a un numero.
Chris Jester-Young,

3
@KennyLau A proposito, hai abbastanza rappresentante per parlare in chat . Non esitate a discutere idee lì, piuttosto che ottenere continuamente rifiutati i tuoi suggerimenti di modifica. ;-)
Chris Jester-Young,

Ho anche abbastanza rappresentante per commentare ora. Perl non ha un modello per [az]?
Leaky Nun,

1
@KennyLau Non a mia conoscenza.
Chris Jester-Young,

3

Lua , 185 187 195 183 166 byte ( provalo online ) [usato regex]

Grazie a @Chris Jester-Young per la regex migliorata.

Grazie a @Katenkyo per averlo ridotto a 166 byte.

golfed:

r={0,0,0,0}for u in(...):gsub("([+-])(%a)","%11%2"):gmatch("-?[%d.]+%a?")do n,i=u:match("(.+)(%a)")r[i and(" ijk"):find(i)or 1]=(n or u)end print(table.concat(r," "))

Ungolfed:

n = "42i+j-k+0.7"

result = {0,0,0,0}

for unit in n:gsub("([+-])(%a)","%11%2"):gmatch("-?[%d.]+%a?") do
  num, index = unit:match("(.+)(%a)")
  if index == "i" then
    result[2] = num
  elseif index == "j" then
    result[3] = num
  elseif index == "k" then
    result[4] = num
  else
    result[1] = unit
  end
end

print(table.concat(result," "))

2
Ciao Kenny, grazie per la soluzione. Di solito non consentiamo che l'input inizi in una variabile (come nin questo caso), quindi dovresti aggiungere il codice per leggere l'input.
Isaacg,

Dovresti essere in grado di salvare alcuni byte modificando l'input da STDIN in argomento, anziché io.read()utilizzare (...).
Punterà

1
Inoltre, l'output richiesto può essere qualsiasi cosa, purché possa essere interpretato dagli umani come un elenco, in modo da poter rimuovere la formattazione aggiuntiva. Compresi alcuni spazi bianchi che puoi radere, il tuo codice può scendere a 166 byte ->r={0,0,0,0}for u in(...):gsub("([+-])(%a)","%11%2"):gmatch("-?[%d.]+%a?")do n,i=u:match("(.+)(%a)")r[i and(" ijk"):find(i)or 1]=(n or u)end print(table.concat(r," "))
Katenkyo

3

C, 236 byte

char j,n[9][9],s[9],y[9],i=8,k,*p=n[8];main(c){for(**n=48;c=getchar(),c+1;)c-32&&(c<46&&(k&&(y[1]=i),k=0,s[--i]=c-43,p=n[i])||c>57&&(k||(*p=49),k=0,y[c-103]=i)||(*p++=c,k=1));for(k&&(y[1]=i);++j<5;)printf("%c%s ",s[y[j]]?45:0,n[y[j]]);}

(Per valori come -0 o -0.0, anche il segno meno viene stampato nell'output, ma poiché la sfida afferma che "puoi cambiare i numeri in un altro modulo dopo aver analizzato se lo desideri" e se -0 appare nell'input, ne consegue che è accettabile anche nell'output. @GamrCorps ha ora chiarito che questo è ok.)


3

JavaScript (ES6), 103 100 byte

f=s=>s.replace(/(?=.)(\+|-|)([\d.]*)(\w?)/g,(_,s,x,c)=>a[c.charCodeAt()&3]=+(s+(x||1)),a=[0,0,0,0])&&a

Modifica: ho salvato 3 byte passando da parseInta charCodeAt, che ha semplicemente bisogno &3di farmi l'indice di array corretto.


Bella idea analizzata + mod. Pensando a base e prefisso
edc65

1

JavaScript (ES6) 106

s=>(s.replace(/([+-]?)([\d.]*)(\w?)/g,(a,b,c,d)=>a&&(s[d||9]=b+(c||1)),s={}),[...'9ijk'].map(i=>+s[i]||0))

Test

f=s=>(s.replace(/([+-]?)([\d.]*)(\w?)/g,(a,b,c,d)=>a&&(s[d||9]=b+(c||1)),s={}),[...'9ijk'].map(i=>+s[i]||0))

function Test()
{
  var t,k,r,ts=TS.value.split('\n')
  
  O.textContent=ts.map(x=>x.trim()&&(
    [t,k]=x.split('=>').map(x=>x.trim()),
    console.log(t,'*',k),
    k=k.match(/[\d+-.]+/g).map(x=>+x),
    r=f(t),
    t+' => '+r+(r+''==k+''?' OK':' KO (check: '+k+')')
  )).join('\n')
}    

Test()
#TS { width:90%; height:10em}
<pre id=O></pre>

Test data (modify if you like)<button onclick='Test()'>repeat test</button>
<textarea id=TS>
1+2i+3j+4k             => [1 2 3 4]
-1+3i-3j+7k            => [-1 3 -3 7]
-1-4i-9j-2k            => [-1 -4 -9 -2]
17-16i-15j-14k         => [17 -16 -15 -14]
  
7+2i                   => [7 2 0 0]
2i-6k                  => [0 2 0 -6]
1-5j+2k                => [1 0 -5 2]
3+4i-9k                => [3 4 0 -9]
  
42i+j-k                => [0 42 1 -1]
6-2i+j-3k              => [6 -2 1 -3]
1+i+j+k                => [1 1 1 1]
-1-i-j-k               => [-1 -1 -1 -1]
  
16k-20j+2i-7           => [-7 2 -20 16]
i+4k-3j+2              => [2 1 -3 4]
5k-2i+9+3j             => [9 -2 3 5]
5k-2j+3                => [3 0 -2 5]
  
1.75-1.75i-1.75j-1.75k => [1.75 -1.75 -1.75 -1.75]
2.0j-3k+0.47i-13       => [-13 0.47 2.0 -3]
5.6-3i                 => [5.6 -3 0 0]
k-7.6i                 => [0 -7.6 0 1]
  
0                      => [0 0 0 0]
0j+0k                  => [0 0 0 0]
-0j                    => [0 0 0 0]
1-0k                   => [1 0 0 0]
</textarea>


0

PowerShell, 178 byte

param($a);$p="(-?)([\d.]+)?";$g={param($s)if($a-match"$p$s"){if(($r=$matches)[2]){$r[1]+$r[2]}else{$r[1]+1}}else{0}};$a-match"$p(\+|-|$)">$null;+$matches[2];"i","j","k"|%{&$g $_}

Ungolfed con Spiegazione

# Get the whole string into a variable
param($a)
# Pattern shared getting both imaginary and real numbers. 
$p="(-?)([\d.]+)?"
# Anonymous function that will locate a imaginary number using a letter sent as a parameter. 
# If no value is assigned a signed 1 is returned. If no value is matched 0 is returned
$g={param($s)if($a-match"$p$s"){if(($r=$matches)[2]){$r[1]+$r[2]}else{$r[1]+1}}else{0}}
# Locate the real component if any. Null is converted to 0
$a-match"$p(\+|-|$)">$null;+$matches[2]
# Call the anonymous function using each of the imaginary suffixes.                                               
"i","j","k"|%{&$g $_}

Non molto impressionato ma è comunque un risultato.


0

PHP, 179 byte

$a=[''=>0,'i'=> 0,'j'=>0,'k'=>0];preg_match_all("/([-+]?)(\d*(\.\d+)?)([ijk]?)/",$argv[1],$m,2);foreach($m as$n)if($n[0])$a[$n[4]]=$n[1].($n[2]===''?1:$n[2]);echo implode(',',$a);

Prova la suite di test .


0

Python 3.5 - 496 byte [usando le espressioni regolari]:

from re import*
def wq(r):
 a=sub('[+](?![0-9])','+1',sub('[-](?![0-9])','-1',r));q=lambda x:(not x.isdigit(),''.join(filter(str.isalpha,x)))
 for z in findall('(?<![0-9])[a-z]',a):a=a.replace(z,('+1{}'.format(z)))
 if not str(sorted(((sub('[.]','',sub('[+-]',' ',a))).split(' ')),key=q)[0]).isdigit():a+='+0, '
 for i in list(set(findall('[a-z]',a))^{'i','j','k'}):a+='+0{}, '.format(i)
 print(findall('[-]?\d+(?:\.\d+)?',''.join(sorted(sub('(?<=[A-Za-z0-9])(?=[+-])',', ',a).split(' '),key=q))))

Potrebbe essere lungo, ma a mia difesa, funziona perfettamente nel fare ciò che l'OP vuole, poiché tutti i casi di test forniti hanno avuto successo usando il mio codice.

Versione non golfata con spiegazione inclusa:

from re import*
def w(r):
    # Substitute all minus (-) and plus (+) signs NOT followed by a number  (if there are any) with a "-1"/"+1", respectively.
    a=sub('[+](?![0-9])','+1',sub('[-](?![0-9])','-1',r))
    # Lambda function created for later use to sort the Quaternion. This function, when given as a key to the "sorted" function, arranges the input Quaternion in the order where the whole number comes first, and then the rest are placed in order of increasing letter value (i,j,k in this case) 
    q=lambda x:(not x.isdigit(),''.join(filter(str.isalpha,x)))
    # The following "for" loop replaces the letters NOT preceded by a number with a one followed by that letter
    for z in findall('(?<![0-9])[a-z]',a):
        a=a.replace(z,('+1{}'.format(z)))
    # The following first substitutes all pluses and minuses (+ and -) with a space, and then that new string is split at those spaces, and returned as a list. After that, the list is sorted according the the "lambda" function shown above. Then, the first item in that list, which is supposed to be a lone number, is checked to make sure that it indeed is a lone number. If it isn't, then "+0, " is appended to the Quaternion. 
    if not str(sorted(((sub('[.]','',sub('[+-]',' ',a))).split(' ')),key=q)[0]).isdigit():
        a+='+0, '
    # The following "for" loop finds ALL the letters NOT in the list, by finding the symmetric difference between a set of all the letters found, and a set containing all the letters needed. For the letters not in the list, a '+0' is added the quaternion, followed by that letter, and then a comma and a space.
    for i in list(set(findall('[a-z]',a))^{'i','j','k'}):
        a+='+0{}, '.format(i)
    # Finally, in this last step, a ", " is added IN BETWEEN unicode characters and pluses/minuses (+/-). Then, it splits at those spaces, and the commas separate different parts of the Quaternion from each other (otherwise, you would get something like `12i+3j+4k` from `2i+3j+4k+1`) in a returned list. Then, that list is sorted according to the lambda expression "q" (above), and then, finally, the NUMBERS (of any type, courtesy to Regex) are extracted from that joined list, and printed out in the correct order.
    print(findall('[-]?\d+(?:\.\d+)?',''.join(sorted(sub('(?<=[A-Za-z0-9])(?=[+-])',', ',a).split(' '),key=q))))

Se quanto sopra è un po 'troppo difficile da leggere, fondamentalmente ciò che sta accadendo è che:

  1. Se ce ne sono, tutti i segni + o - NON seguiti da un numero vengono sostituiti rispettivamente con un "+1" / "- 1".

  2. Viene lambdadefinita una funzione che, quando utilizzata in una sortedfunzione come chiave, ordina l'elenco in base al mettere prima l'intero numero, quindi ordinando il resto in valore crescente di lettere ("i", quindi "j", quindi "k" in questo caso).

  3. Il Quaternion, che ora ha tutti i segni +/- sostituiti con 1 se necessario, viene cercato, usando le espressioni regolari, per TUTTE le lettere NON precedute da almeno un numero e quelle lettere che corrispondono sono sostituite da un "+1" seguito da quella lettera.

  4. L'istruzione "if" sostituisce quindi TUTTI i segni +/- con uno spazio, quindi il Quaternion modificato viene ora "diviso" in quegli spazi e restituito in un elenco. Quindi, l'elenco viene ordinato in base alla funzione lambda spiegata in precedenza. Infine, il primo elemento in quell'elenco viene verificato per assicurarsi che sia un numero, dal momento che dovrebbe essere, e se non lo è, allora un "+0" viene aggiunto al Quaternion.

  5. Il secondo ciclo "for" trova TUTTE le lettere NON nel Quaternione trovando una differenza simmetrica tra un insieme di quelle lettere trovate nell'espressione e quindi un insieme che include tutte le lettere richieste. Se ne vengono trovati, quindi un "+0" seguito dalla lettera mancante e uno spazio viene aggiunto al Quaternion.

  6. Infine, in quest'ultimo passaggio, viene aggiunto un "," tra ciascun carattere seguito da un simbolo +/-, quindi il Quaternion viene diviso in quegli spazi, quindi l'elenco restituito viene ordinato, per l'ultima volta, in base al funzione lambda definita come "q" in precedenza. Le virgole nell'espressione separano ogni parte del quaternione (altrimenti, otterresti qualcosa di simile 14i+5j+6kda 4i+5j+6k+1). Infine, quell'elenco ora ordinato viene unito in una stringa e solo i numeri di qualsiasi tipo (per gentile concessione delle espressioni regolari) vengono estratti e infine restituiti in un elenco, nell'ordine corretto, ogni volta.

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.