Parse a Quaternion

27

Si aún no lo sabe, un cuaternión es básicamente un número de 4 partes. Para los propósitos de este desafío, tiene un componente real y tres componentes imaginarios . Los componentes imaginarios son representados por el sufijo i, j, k. Por ejemplo, 1-2i+3j-4kes un cuaternión con 1ser el componente real y -2, 3y -4siendo los componentes imaginarios.

En este desafío, debe analizar la forma de cadena de un cuaternión (ej. "1+2i-3j-4k") En una lista / matriz de coeficientes (ej. [1 2 -3 -4]). Sin embargo, la cadena de cuaternión se puede formatear de muchas maneras diferentes ...

  • Puede ser normal: 1+2i-3j-4k
  • Puede tener términos que faltan: 1-3k, 2i-4k(Si usted tiene términos que faltan, la producción 0de tales condiciones)
  • Puede haber coeficientes falta: i+j-k(En este caso, esto es equivalente a 1i+1j-1kEn otras palabras, a. i, jO ksin un número delante se asume que tiene un 1frente de forma predeterminada)
  • Puede no estar en el orden correcto: 2i-1+3k-4j
  • Los coeficientes pueden ser simplemente enteros o decimales: 7-2.4i+3.75j-4.0k

Hay algunas cosas a tener en cuenta al analizar:

  • Siempre habrá un +o -entre términos
  • Siempre se le pasará una entrada válida con al menos 1 término y sin letras repetidas (sin j-js)
  • Se puede suponer que todos los números son válidos
  • Puede cambiar los números en otra forma después de analizar si lo desea (por ej. 3.0 => 3, 0.4 => .4, 7 => 7.0)

Se prohíben las funciones integradas de análisis / cuaternión y las lagunas estándar. Esto incluye evalpalabras clave y funciones. La entrada será una sola cadena y la salida será una lista, una matriz, valores separados por espacios en blanco, etc.

Como se trata de , gana el código más corto en bytes.

Toneladas de casos de prueba

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
fuente
¿Alguna vez habrá +signos innecesarios en la entrada? Así como: +1k?
FryAmTheEggman
@FryAmTheEggman No. las entradas nunca comenzarán con a +.
GamrCorps
1
¿ -0Forma parte del resultado legal de los dos últimos ejemplos?
isaacg
1
@isaacg sí, eso está bien
GamrCorps
1
@LLlAMnYP Sacas un buen punto. Vamos a definir la evalrestricción que se toma en una cadena, se interpreta como código y / o entrada. Cualquier conversión no cuenta bajo esto porque no puede pasar, por ejemplo, la cadena "test"a una función de conversión de enteros para recibir un entero, pero testse interpretaría como código en una evalfunción normal . TLDR: eval: no, conversiones de tipo: sí.
GamrCorps

Respuestas:

5

Pyth, 48 bytes

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

Conjunto de pruebas de demostración

El formato de salida está separado por una nueva línea. El código del conjunto de pruebas utiliza la separación de espacios, para facilitar la lectura, pero por lo demás es el mismo.

Salidas a -0en los últimos 2 casos, lo que espero que esté bien.

Explicación a seguir.

isaacg
fuente
9

Retina, 115

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

S`[ijk+]+

Pruébalo en línea!

1 byte guardado gracias a @Chris Jester-Young .

Un error corregido y 6 bytes guardados gracias a @Martin Büttner

Encontramos un par de errores que involucran algunos casos extremos, aumentaron bastante el recuento de bytes.

Devuelve los números de nueva línea separados. De todos modos, esta tiene una solución principalmente elegante que se ve arruinada por los casos extremos, pero bueno, tuve que usar el modo de clasificación, eso significa que usé la herramienta adecuada para el trabajo, ¿verdad?

Explicación:

Etapa por etapa, como siempre.

\b[ijk]
1$&

Los únicos caracteres en la entrada que pueden crear límites de palabras son -+.. Esto significa que si encontramos un límite seguido de una letra, tenemos un implícito 1que agregamos con el reemplazo. $&es sinónimo de $0.

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

Muchas gracias a Martin por este, este agrega lo implícito 0para la parte real si faltaba en la entrada. Nos aseguramos de que no podamos encontrar un número seguido de un signo más o menos, o el final de la cadena. Todos los números complejos tendrán una letra después de ellos.

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

Las siguientes 3 etapas son más o menos lo mismo, salvo en qué letra impactan. Todos miran para ver si no podemos coincidir con la letra, y si no podemos agregarle un 0término. La única razón itiene un extra +antes de evitar que el valor real sea ilegible con el icoeficiente s, los otros números están separados por su variable compleja.

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

Ah, la parte divertida. Esto utiliza la etapa de ordenación más nueva, indicada por Ola opción anterior al separador de opciones. El truco aquí es tomar el número entero seguido opcionalmente por un carácter de palabra, que en este caso solo coincidirá con uno de ijk. La otra opción utilizada es la $que hace que el valor utilizado para ordenar estas coincidencias sea el reemplazo. Aquí solo usamos la letra opcional que queda como nuestro valor de clasificación. Dado que Retina ordena lexicográficamente por defecto, los valores se ordenan como si estuvieran en un diccionario, lo que significa que ordenamos las coincidencias "", "i", "j", "k".

-
+-

Esta etapa pone un +signo delante de todos los signos menos, esto es necesario si tenemos un valor negativo para ien la etapa dividida, más adelante.

^ \ +

Eliminamos el inicio +para asegurarnos de que no tenemos una nueva línea adicional.

S`[ijk+]+

Divida las líneas restantes en ejecuciones de las variables complejas o el signo más. Esto nos da un valor por línea.

FryAmTheEggman
fuente
3

Perl 5, 125 bytes

#!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
fuente
1
@KennyLau Lamentablemente, su cambio propuesto no hace lo que espera. He intentado eso antes de publicar mi respuesta. ;-)
Chris Jester-Young
@KennyLau Con respecto a este cambio propuesto , las \acoincidencias de Perl en "alarma", no alfabética. Hay \wpara palabra-carácter (alfanumérico y subrayado), pero eso no funcionará aquí; Necesitamos que no coincida con un número.
Chris Jester-Young
3
@KennyLau Por cierto, tienes suficiente representante para hablar en el chat . Siéntase libre de discutir ideas allí, en lugar de rechazar continuamente sus sugerencias de edición. ;-)
Chris Jester-Young
También tengo suficiente representante para comentar ahora. ¿Perl no tiene un patrón para [az]?
Leaky Nun
1
@KennyLau No que yo sepa.
Chris Jester-Young
3

Lua , 185 187 195 183 166 bytes ( pruébelo en línea ) [regex usado]

Gracias a @Chris Jester-Young por la expresión regular mejorada.

Gracias a @Katenkyo por reducirlo a 166 bytes.

Golfizado:

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

Sin 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," "))
Monja permeable
fuente
2
Hola Kenny, gracias por la solución. Por lo general, no permitimos que la entrada comience en una variable (como nen este caso), por lo que debe agregar el código para leer la entrada.
isaacg
Debería poder guardar algún byte cambiando su entrada de STDIN a argumento, en lugar de io.read()usar (...).
Apuntará
1
Además, la salida solicitada puede ser cualquier cosa, siempre que los humanos puedan interpretarla como una lista, por lo que puede eliminar el formato adicional. Incluyendo algunos espacios en blanco más que puede eliminar, su código puede bajar a 166 bytes ->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 bytes

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

(Para valores como -0 o -0.0, el signo menos también se imprime en la salida, pero dado que el desafío establece que "puede cambiar los números a otra forma después de analizar si lo desea", y si aparece -0 en la entrada, se deduce que también es aceptable en la salida. @GamrCorps ahora ha aclarado que esto está bien).

MIllIbyte
fuente
3

JavaScript (ES6), 103100 bytes

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

Editar: guardé 3 bytes al cambiar de parseInta charCodeAt, que convenientemente solo necesita &3obtener el índice de matriz correcto.

Neil
fuente
Buena idea parseInt + mod. Pensando en la base y el prefijo
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))

Prueba

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
fuente
0

PowerShell, 178 bytes

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 explicación

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

No super impresionado pero es un resultado no obstante.

Mate
fuente
0

PHP, 179 bytes

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

Prueba el conjunto de pruebas .

nickb
fuente
0

Python 3.5 - 496 bytes [usando expresiones regulares]:

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

Puede ser largo, pero en mi defensa, funciona perfectamente para hacer lo que el OP quiere, ya que todos los casos de prueba fueron exitosos usando mi código.

Versión sin golf con explicación incluida:

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 lo anterior es un poco difícil de leer, básicamente lo que está sucediendo es que:

  1. Si hay alguno, todos los signos + o - NO seguidos de un número se reemplazan con un "+1" / "- 1", respectivamente.

  2. Se lambdadefine una función que, cuando se usa en una sortedfunción como una tecla, clasifica la lista de acuerdo con poner primero el número entero y luego ordenar el resto en letras cada vez mayores ("i", luego "j", luego "k" en este caso).

  3. El Quaternion, que ahora tiene todos los signos +/- reemplazados por un 1 si es necesario, se busca, usando Expresiones regulares, para TODAS las letras NO precedidas por al menos un número, y aquellas letras que coinciden se reemplazan con un "+1" seguido de esa carta.

  4. La instrucción "if" luego reemplaza TODOS los signos +/- con un espacio, y luego el Quaternion modificado ahora se "divide" en esos espacios y se devuelve en una lista. Luego, la lista se ordena de acuerdo con la función lambda explicada anteriormente. Finalmente, el primer elemento de esa lista se verifica para asegurarse de que sea un número, ya que se supone que es, y si no lo es, se agrega un "+0" al Quaternion.

  5. El segundo ciclo "for" encuentra TODAS las letras que NO están en el Quaternion al encontrar una diferencia simétrica entre un conjunto de esas letras encontradas en la expresión, y luego un conjunto que incluye todas las letras requeridas. Si se encuentra alguno, se agrega un "+0" seguido de la letra que falta y se agrega un espacio al Quaternion.

  6. Finalmente, en este último paso, se agrega un "," entre cada carácter seguido de un símbolo +/-, y luego el Quaternion se divide en esos espacios, luego la lista devuelta se ordena, por última vez, de acuerdo con el función lambda definida como "q" anteriormente. Las comas en la expresión separan cada parte del cuaternión (de lo contrario, obtendría algo como 14i+5j+6kde 4i+5j+6k+1). Por último, esa lista ahora ordenada se une en una cadena, y solo se extraen los números de cualquier tipo (cortesía de Expresiones regulares), y finalmente se devuelven en una lista, en el orden correcto, cada vez.

R. Kap
fuente