Analyser un quaternion

27

Si vous ne le savez pas déjà, un quaternion est essentiellement un numéro en 4 parties. Aux fins de ce défi, il a une composante réelle et trois composantes imaginaires . Les composantes imaginaires sont représentées par le suffixe i, j, k. Par exemple, 1-2i+3j-4kest un quaternion avec 1être la composante réelle et -2, 3et -4étant les composantes imaginaires.

Dans ce défi, vous devez analyser la forme de chaîne d'un quaternion (ex. "1+2i-3j-4k") Dans une liste / tableau de coefficients (ex. [1 2 -3 -4]). Cependant, la chaîne quaternion peut être formatée de différentes manières ...

  • Cela peut être normal: 1+2i-3j-4k
  • Il peut avoir des termes manquants: 1-3k, 2i-4k(Si vous avez des termes manquants, la sortie 0de ces termes)
  • Il peut avoir des coefficients manquant: i+j-k(Dans ce cas, cela équivaut à 1i+1j-1kAutrement dit, un. i, jOu ksans numéro devant est supposé avoir une 1face par défaut)
  • Ce n'est peut-être pas dans le bon ordre: 2i-1+3k-4j
  • Les coefficients peuvent être simplement des entiers ou des décimales: 7-2.4i+3.75j-4.0k

Il y a certaines choses à noter lors de l'analyse:

  • Il y aura toujours un +ou -entre les termes
  • Vous recevrez toujours une entrée valide avec au moins 1 terme et sans lettres répétées (pas de j-js)
  • Tous les nombres peuvent être considérés comme valides
  • Vous pouvez modifier les numéros dans une autre forme après l' analyse syntaxique si vous voulez (ex. 3.0 => 3, 0.4 => .4, 7 => 7.0)

Les analyses intégrées / quaternion et les failles standard sont interdites. Cela inclut les evalmots clés et les fonctions. L'entrée sera une chaîne unique et la sortie sera une liste, un tableau, des valeurs séparées par des espaces, etc.

Comme il s'agit de , le code le plus court en octets l'emporte.

Des tonnes de cas de 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]
GamrCorps
la source
Y aura-t-il jamais des +signes inutiles dans l'entrée? Comme +1k:?
FryAmTheEggman
Les entrées @FryAmTheEggman No. ne commenceront jamais par a +.
GamrCorps
1
Est-ce -0une partie de la production légale pour les deux derniers exemples?
isaacg
1
@isaacg oui ça va
GamrCorps
1
@LLlAMnYP Vous soulevez un bon point. Permet de définir la evalrestriction à prendre dans une chaîne, interprète comme code et / ou entrée. Toutes les conversions ne comptent pas en dessous car vous ne pouvez pas passer, par exemple, la chaîne "test"à une fonction de conversion d'entier pour recevoir un entier, mais testserait interprété comme du code dans une evalfonction normale . TLDR: eval: non, conversions de type: oui.
GamrCorps

Réponses:

5

Pyth, 48 octets

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

Suite de tests de démonstration

Le format de sortie est séparé par des sauts de ligne. Le code de la suite de tests utilise la séparation d'espace pour faciliter la lecture, mais est par ailleurs le même.

Sorties a -0dans les 2 derniers cas, ce qui, je l'espère, est OK.

Explication à suivre.

isaacg
la source
9

Rétine, 115

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

S`[ijk+]+

Essayez-le en ligne!

1 octet économisé grâce à @Chris Jester-Young .

Un bug corrigé et 6 octets enregistrés grâce à @Martin Büttner

J'ai trouvé quelques bogues impliquant des cas marginaux, le nombre d'octets a augmenté un peu.

Renvoie les numéros séparés par une nouvelle ligne. Quoi qu'il en soit, cela a une solution principalement élégante qui est en quelque sorte ruinée par les cas marginaux, mais bon, j'ai dû utiliser le mode de tri, cela signifie que j'ai utilisé le bon outil pour le travail, non?

Explication:

Étape par étape, comme d'habitude.

\b[ijk]
1$&

Les seuls caractères de l'entrée pouvant créer des limites de mots sont -+.. Cela signifie que si nous trouvons une limite suivie d'une lettre, nous avons un implicite 1que nous ajoutons au remplacement. $&est synonyme de $0.

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

Un grand merci à Martin pour celui-ci, celui-ci ajoute l'implicite 0pour la partie réelle s'il manquait à l'entrée. Nous nous assurons que nous ne pouvons pas trouver un nombre suivi d'un signe plus ou moins, ou la fin de la chaîne. Tous les nombres complexes auront une lettre après eux.

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

Les 3 étapes suivantes sont toutes à peu près les mêmes, sauf dans quelle lettre elles ont un impact. Tous cherchent à voir si nous ne pouvons pas faire correspondre la lettre, et si nous ne pouvons pas, nous pouvons ajouter un 0terme pour cela. La seule raison ia un supplément +avant de ne pas pouvoir lire la valeur réelle avec le icoefficient s, les autres nombres sont tous séparés par leur variable complexe.

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

Ah, la partie amusante. Cela utilise la nouvelle étape de tri, indiquée par le Osigne avant le séparateur d'options. L'astuce consiste à saisir le nombre entier suivi éventuellement d'un caractère de mot, qui dans ce cas ne correspondra qu'à l'un des ijk. L'autre option utilisée est celle $qui fait que la valeur utilisée pour trier ces correspondances est le remplacement. Ici, nous utilisons simplement la lettre facultative restante comme valeur de tri. Étant donné que Retina trie lexicographiquement par défaut, les valeurs sont triées comme elles le seraient dans un dictionnaire, ce qui signifie que nous obtenons les correspondances dans l' "", "i", "j", "k"ordre.

-
+-

Cette étape met un +signe devant tous les signes moins, cela est nécessaire si nous avons une valeur négative pour idans l'étape divisée, plus tard.

^ \ +

Nous supprimons le début +pour nous assurer que nous n'avons pas de nouvelle ligne supplémentaire.

S`[ijk+]+

Fractionnez les lignes restantes lors des exécutions des variables complexes ou du signe plus. Cela nous donne bien une valeur par ligne.

FryAmTheEggman
la source
3

Perl 5, 125 octets

#!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)}/
Chris Jester-Young
la source
1
@KennyLau Malheureusement, votre changement proposé ne fait pas ce que vous attendez. J'ai essayé avant de poster ma réponse. ;-)
Chris Jester-Young
@KennyLau Concernant ce changement proposé , Perl \acorrespond à "alarm", pas alphabétique. Il y a \wpour le mot-caractère (alphanumérique et souligné), mais cela ne fonctionnera pas ici; nous en avons besoin pour ne pas correspondre à un nombre.
Chris Jester-Young
3
@KennyLau BTW, vous avez suffisamment de représentants pour parler dans le chat . N'hésitez pas à discuter des idées là-bas, plutôt que de constamment rejeter vos suggestions de modification. ;-)
Chris Jester-Young
J'ai également assez de représentants pour commenter maintenant. Perl n'a-t-il pas de modèle pour [az]?
Leaky Nun
1
@KennyLau Pas à ma connaissance.
Chris Jester-Young
3

Lua , 185 187 195 183 183 166 octets ( essayez en ligne ) [expression régulière utilisée]

Merci à @Chris Jester-Young pour la regex améliorée.

Merci à @Katenkyo de l'avoir ramené à 166 octets.

Golfé:

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," "))

Non golfé:

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," "))
Leaky Nun
la source
2
Salut Kenny, merci pour la solution. Habituellement, nous n'autorisons pas l'entrée commençant dans une variable (comme ndans ce cas), vous devez donc ajouter le code pour lire l'entrée.
isaacg
Vous devriez pouvoir enregistrer un octet en changeant votre entrée de STDIN en argument, au lieu de io.read(), use (...). Il pointera vers le premier argument de ligne de commande et vous permettra d'économiser 4 octets supplémentaires :)
Katenkyo
1
En outre, la sortie demandée peut être n'importe quoi, tant qu'elle peut être interprétée par les humains comme une liste, vous pouvez donc supprimer la mise en forme supplémentaire. Y compris quelques espaces supplémentaires que vous pouvez raser, votre code peut descendre à 166 octets ->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 octets

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]]);}

(Pour les valeurs telles que -0 ou -0,0, le signe moins est également imprimé dans la sortie, mais comme le défi indique que "vous pouvez changer les nombres sous une autre forme après avoir analysé si vous le souhaitez", et si -0 apparaît dans l'entrée, il s'ensuit que c'est également acceptable dans la sortie. @GamrCorps a maintenant clarifié que c'est ok.)

mIllIbyte
la source
3

JavaScript (ES6), 103 100 octets

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

Edit: sauvé 3 octets en passant de parseIntà charCodeAt, ce qui a juste besoin &3de me fournir l'index de tableau correct.

Neil
la source
Belle idée parseInt + mod. Réflexion sur la base et le préfixe
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))

Tester

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>

edc65
la source
0

PowerShell, 178 octets

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 $_}

Non golfé avec explication

# 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 $_}

Pas super impressionné mais c'est quand même un résultat.

Mat
la source
0

PHP, 179 octets

$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);

Essayez la suite de tests .

nickb
la source
0

Python 3.5 - 496 octets [en utilisant des expressions régulières]:

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))))

Cela peut être long, mais pour ma défense, cela fonctionne parfaitement pour faire ce que le PO souhaite, car tous les cas de test donnés ont réussi à utiliser mon code.

Version non golfée avec explication incluse:

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))))

Si ce qui précède est un peu trop difficile à lire, ce qui se passe est que:

  1. S'il y en a, tous les signes + ou - NON suivis d'un nombre sont remplacés respectivement par "+1" / "- 1".

  2. Une lambdafonction est définie qui, lorsqu'elle est utilisée dans une sortedfonction comme clé, trie la liste en mettant d'abord le nombre entier, puis en ordonnant le reste en augmentant la valeur de la lettre ("i", puis "j", puis "k" dans ce cas).

  3. Le Quaternion, ayant maintenant tous les signes +/- remplacés par un 1 si nécessaire, est recherché, en utilisant des expressions régulières, pour TOUTES les lettres NON précédées d'au moins un chiffre, et les lettres qui correspondent sont remplacées par un "+1" suivi de cette lettre.

  4. L'instruction "if" remplace alors TOUS les signes +/- par un espace, puis le Quaternion modifié est maintenant "divisé" à ces espaces, et renvoyé dans une liste. Ensuite, la liste est triée selon la fonction lambda expliquée précédemment. Enfin, le premier élément de cette liste est vérifié pour s'assurer qu'il s'agit d'un nombre, car il est censé l'être, et si ce n'est pas le cas, un "+0" est ajouté au Quaternion.

  5. La deuxième boucle "for" trouve TOUTES les lettres NON dans le Quaternion en trouvant une différence symétrique entre un ensemble de ces lettres trouvées dans l'expression, puis un ensemble comprenant toutes les lettres requises. S'il en trouve, un "+0" suivi de la lettre manquante et un espace sont ajoutés au Quaternion.

  6. Enfin, dans cette dernière étape, un "," est ajouté entre chaque caractère suivi d'un symbole +/-, puis le Quaternion est divisé à ces espaces, puis la liste renvoyée est triée, pour la dernière fois, selon le fonction lambda définie comme "q" plus tôt. Les virgules dans l'expression séparent chaque partie du quaternion (sinon, vous obtiendrez quelque chose comme 14i+5j+6kde 4i+5j+6k+1). Enfin, cette liste maintenant triée est réunie en une chaîne, et seuls les numéros de tout type (gracieuseté des expressions régulières) sont extraits et finalement renvoyés dans une liste, dans le bon ordre, à chaque fois.

R. Kap
la source