Analisar um Quaternion

27

Se você ainda não sabe, um quaternion é basicamente um número de 4 partes. Para os propósitos deste desafio, ele possui um componente real e três componentes imaginários . Os componentes imaginários são representados pelo sufixo i, j, k. Por exemplo, 1-2i+3j-4ké um Quatérnion com 1sendo a componente real e -2, 3, e -4sendo os componentes imaginárias.

Neste desafio, você deve analisar a forma de string de um quaternion (ex. "1+2i-3j-4k") Em uma lista / matriz de coeficientes (ex. [1 2 -3 -4]). No entanto, a cadeia de quaterniões pode ser formatada de várias maneiras diferentes ...

  • Pode ser normal: 1+2i-3j-4k
  • Ele pode ter termos ausentes: 1-3k, 2i-4k(Se você tem termos ausentes, a saída 0para esses termos)
  • Pode ter coeficientes faltando: i+j-k(Neste caso, isso é equivalente a 1i+1j-1kEm outras palavras, a. i, jOu ksem um número na frente Assume-se que um 1em frente por padrão)
  • Pode não estar na ordem correta: 2i-1+3k-4j
  • Os coeficientes podem ser simplesmente números inteiros ou decimais: 7-2.4i+3.75j-4.0k

Há algumas coisas a serem observadas ao analisar:

  • Sempre haverá um +ou -entre termos
  • Você sempre receberá entrada válida com pelo menos 1 termo e sem letras repetidas (no j-js)
  • Todos os números podem ser considerados válidos
  • Você pode mudar os números em uma outra forma após a análise se você quiser (ex. 3.0 => 3, 0.4 => .4, 7 => 7.0)

Os recursos de análise / quaternião e brechas padrão não são permitidos. Isso inclui evalpalavras-chave e funções. A entrada será uma única sequência e a saída será uma lista, uma matriz, valores separados por espaços em branco, etc.

Como esse é o , o código mais curto em bytes vence.

Toneladas de casos de teste

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
fonte
Alguma vez haverá +sinais desnecessários na entrada? Como +1k:?
FryAmTheEggman 30/03
As entradas @FryAmTheEggman No. nunca começarão com a +.
GamrCorps
1
Faz -0parte da produção legal dos dois últimos exemplos?
Isaacg
1
@isaacg sim que é bom
GamrCorps
1
@LLlAMnYP Você traz um bom argumento. Vamos definir a evalrestrição a ser aceita em uma string, interpreta como código e / ou entrada. Todas as conversões não contam com isso porque você não pode passar, por exemplo, a string "test"para uma função de conversão de número inteiro para receber um número inteiro, mas testseria interpretada como código em uma evalfunção normal . TLDR: eval: não, digite conversões: sim.
GamrCorps

Respostas:

5

Pitão, 48 bytes

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

Conjunto de teste de demonstração

O formato de saída é separado por nova linha. O código do conjunto de testes usa separação de espaço, para facilitar a leitura, mas é o mesmo.

Saídas a -0nos dois últimos casos, o que espero que seja bom.

Explicação a seguir.

isaacg
fonte
9

Retina, 115

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

S`[ijk+]+

Experimente online!

1 byte economizado graças a @Chris Jester-Young .

Um bug corrigido e 6 bytes salvos graças a @Martin Büttner

Encontrei alguns bugs envolvendo alguns casos extremos, aumentamos bastante a contagem de bytes.

Retorna os números nova linha separados. Enfim, isso tem uma solução bastante elegante que é arruinada por casos extremos, mas ei, eu usei o modo de classificação, isso significa que usei a ferramenta certa para o trabalho, certo?

Explicação:

Estágio por estágio, como sempre.

\b[ijk]
1$&

Os únicos caracteres na entrada que podem criar limites de palavras são -+.. Isso significa que, se encontrarmos um limite seguido por uma letra, teremos um implícito 1que adicionamos à substituição. $&é um sinônimo para $0.

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

Muito obrigado a Martin por este, este acrescenta o implícito 0para a parte real, se estava faltando na entrada. Garantimos que não conseguimos encontrar um número seguido por um sinal de mais ou menos, ou o fim da sequência. Todos os números complexos terão uma letra após eles.

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

As próximas três etapas são praticamente as mesmas, exceto em qual letra elas impactam. Todos eles procuram ver se não conseguimos corresponder à letra e, se não podemos, podemos adicionar um 0termo para ela. O único motivo item um extra +antes de impedir que o valor real seja ilegível com o icoeficiente s, os outros números são todos separados por sua variável complexa.

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

Ah, a parte divertida. Isso usa o estágio de classificação newish, indicado pelo Obacktick separador antes da opção. O truque aqui é pegar o número inteiro seguido opcionalmente por um caractere de palavra, que neste caso só corresponderá a um dos ijk. A outra opção usada é o $que faz com que o valor usado para classificar essas correspondências seja a substituição. Aqui, apenas usamos a letra opcional restante como nosso valor de classificação. Como a Retina classifica lexicograficamente por padrão, os valores são classificados como se estivessem em um dicionário, ou seja, ordenamos as correspondências "", "i", "j", "k".

-
+-

Esse estágio coloca um +sinal na frente de todos os sinais de menos, isso é necessário se tivermos um valor negativo para ino estágio dividido posteriormente.

^ \ +

Removemos o líder +para garantir que não tenhamos uma nova linha de líder extra.

S`[ijk+]+

Divida as linhas restantes nas execuções das variáveis ​​complexas ou no sinal de mais. Isso nos dá um valor por linha.

FryAmTheEggman
fonte
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
fonte
1
@KennyLau Infelizmente, sua alteração proposta não faz o que você espera. Eu tentei isso antes de postar minha resposta. ;-)
Chris Jester-Young
@KennyLau Em relação a essa alteração proposta , as \acorrespondências de Perl no "alarme", não no alfabético. Há \wcaracteres de palavras (alfanuméricos e sublinhados), mas isso não funciona aqui; precisamos que ele não corresponda a um número.
Chris Jester-Young
3
@KennyLau BTW, você tem representante suficiente para conversar no chat . Sinta-se à vontade para discutir idéias lá, em vez de rejeitar continuamente suas sugestões de edição. ;-)
Chris Jester-Young
Eu também tenho representante suficiente para comentar agora. O Perl não possui um padrão para [az]?
Freira vazada
1
@KennyLau Não que eu saiba.
Chris Jester-Young
3

Lua , 185 187 195 183 166 bytes ( experimente online ) [regex usado]

Obrigado a @Chris Jester-Young pelo aprimoramento da regex.

Obrigado a @Katenkyo por reduzi -lo a 166 bytes.

Golfe:

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," "))
Freira Furada
fonte
2
Olá Kenny, obrigado pela solução. Normalmente, não permitimos que a entrada comece em uma variável (como nneste caso); portanto, você deve adicionar o código para ler a entrada.
Isaacg
Você poderá salvar alguns bytes alterando sua entrada de STDIN para argumento, em vez de io.read()usar (...). Ele apontará para o primeiro argumento da linha de comando e permitirá que você salve mais 4 bytes :) #
30516
1
Além disso, a saída solicitada pode ser qualquer coisa, desde que possa ser interpretada por humanos como uma lista, para que você possa remover a formatação extra. Incluindo mais espaços em branco que você pode raspar, seu código pode diminuir para 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 ou -0,0, o sinal de menos também é impresso na saída, mas como o desafio afirma que "você pode alterar números para outro formulário após a análise, se desejar" e se -0 aparecer na entrada, segue-se que também é aceitável na saída. @GamrCorps agora esclareceu que está tudo bem.)

mIllIbyte
fonte
3

JavaScript (ES6), 103 100 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: salvou 3 bytes mudando de parseIntpara charCodeAt, o que convenientemente só precisa &3do índice de matriz correto.

Neil
fonte
Boa ideia parseInt + mod. Pensando base e prefixo
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))

Teste

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
fonte
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 with Explanation

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

Não estou impressionado, mas é um resultado.

Matt
fonte
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);

Experimente a suíte de testes .

nickb
fonte
0

Python 3.5 - 496 bytes [usando expressões 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))))

Pode demorar, mas, em minha defesa, funciona perfeitamente ao fazer o que o OP deseja, pois todos os casos de teste fornecidos foram bem-sucedidos usando meu código.

Versão ungolfed com explicação incluída:

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 o acima é um pouco difícil de ler, basicamente o que está acontecendo é o seguinte:

  1. Se houver algum, todos os sinais + ou - NÃO seguidos de um número são substituídos por um "+1" / "- 1", respectivamente.

  2. lambdaÉ definida uma função que, quando usada em uma sortedfunção como chave, classifica a lista de acordo com a colocação do número inteiro em primeiro lugar e, em seguida, solicita o restante no aumento do valor da letra ("i", depois "j" e "k" Neste caso).

  3. O Quaternion, agora com todos os sinais +/- substituídos por 1, se necessário, é pesquisado, usando Expressões regulares, para TODAS as letras NÃO precedidas por pelo menos um número, e as letras correspondentes são substituídas por um "+1" seguido de "+1" essa carta.

  4. A instrução "if" substitui ALL +/- por um espaço e, em seguida, o Quaternion modificado agora é "dividido" nesses espaços e retornado em uma lista. Em seguida, a lista é classificada de acordo com a função lambda explicada anteriormente. Finalmente, o primeiro item dessa lista é verificado para garantir que seja um número, já que deveria ser, e se não for, um "+0" é adicionado ao Quaternion.

  5. O segundo loop "for" encontra TODAS as letras NOT no Quaternion, encontrando uma diferença simétrica entre um conjunto dessas letras encontradas na expressão e, em seguida, um conjunto incluindo todas as letras necessárias. Se algum for encontrado, um "+0" seguido da letra ausente e um espaço serão adicionados ao Quaternion.

  6. Finalmente, nesta última etapa, um "," é adicionado entre cada caractere seguido de um símbolo +/- e, em seguida, o Quaternion é dividido nesses espaços, e a lista retornada é classificada pela última vez, de acordo com o função lambda definida como "q" anteriormente. As vírgulas na expressão separar cada parte do quaternion (caso contrário, você estaria recebendo algo como 14i+5j+6ka partir 4i+5j+6k+1). Por fim, essa lista agora classificada é unida em uma sequência e apenas os números de qualquer tipo (cortesia de Expressões regulares) são extraídos e finalmente retornados em uma lista, na ordem correta, sempre.

R. Kap
fonte