Разобрать кватернион

27

Если вы еще не знаете, кватернион в основном состоит из 4-х частей. Для целей этой задачи он имеет реальный компонент и три мнимых компонента. Мнимые компоненты представлены суффиксом i, j, k. Например, 1-2i+3j-4kэто кватернион с 1быть реальным компонентом и -2, 3и -4будучи мнимые компоненты.

В этом задании вы должны разобрать строковую форму кватерниона (например "1+2i-3j-4k") в список / массив коэффициентов (например [1 2 -3 -4]). Тем не менее, строка кватерниона может быть отформатирована разными способами ...

  • Это может быть нормально: 1+2i-3j-4k
  • Он может иметь недостающие условия: 1-3k, 2i-4k(если у вас есть недостающие условия, выход 0для этих терминов)
  • Это , возможно, отсутствуют коэффициенты: i+j-k(В данном случае, это эквивалентно 1i+1j-1kДругими словами,. i, jИли kбез номера перед предполагается иметь 1перед по умолчанию)
  • Это может быть не в правильном порядке: 2i-1+3k-4j
  • Коэффициенты могут быть просто целыми или десятичными: 7-2.4i+3.75j-4.0k

Есть некоторые вещи, которые нужно отметить при разборе:

  • Всегда будет +или -между терминами
  • Вам всегда будут переданы правильные данные, по крайней мере, с 1 термином и без повторяющихся букв (без j-js)
  • Все числа можно считать действительными
  • Вы можете изменить номера в другую форму после разбора , если вы хотите (напр. 3.0 => 3, 0.4 => .4, 7 => 7.0)

Встроенные синтаксические / кватернионные и стандартные лазейки запрещены. Это включает в себя evalключевые слова и функции. На входе будет одна строка, а на выходе будет список, массив, значения, разделенные пробелами и т. Д.

Поскольку это , выигрывает самый короткий код в байтах.

Тонны тестовых случаев

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
источник
Будут ли когда-нибудь ненужные +знаки на входе? Нравится +1k:?
FryAmTheEggman
@FryAmTheEggman Нет. Входные данные никогда не начнутся с +.
GamrCorps
1
Является -0ли частью легальной продукции для последних двух примеров?
Исаак
1
@isaacg да, это нормально
GamrCorps
1
@LLlAMnYP Вы подняли хороший вопрос. Позволяет определить evalограничение, которое будет принимать строку, интерпретируется как код и / или ввод. Любые преобразования не учитываются при этом, потому что вы не можете передать, например, строку "test"в целочисленную функцию преобразования, чтобы получить целое число, но testэто будет интерпретироваться как код в нормальной evalфункции. TLDR: eval: нет, преобразования типов: да.
GamrCorps

Ответы:

5

Pyth, 48 байтов

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

Демонстрационный набор тестов

Выходной формат разделен символом новой строки. Код набора тестов использует разделение пробелов для простоты чтения, но в остальном тот же.

Выводы a -0в последних 2 случаях, которые, я надеюсь, в порядке.

Объяснение, чтобы следовать.

isaacg
источник
9

Сетчатка, 115

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

S`[ijk+]+

Попробуйте онлайн!

1 байт сохранен благодаря @Chris Jester-Young .

Исправлена ​​ошибка и 6 байтов сохранено благодаря @Martin Büttner

Нашел пару ошибок, связанных с некоторыми крайними случаями, увеличил количество байтов.

Возвращает разделенные числами новую строку. В любом случае, у этого есть в основном элегантное решение, которое в некотором роде разрушается крайними случаями, но эй, я должен был использовать режим сортировки, это означает, что я использовал правильный инструмент для работы, верно?

Объяснение:

Поэтапно, как обычно.

\b[ijk]
1$&

Единственными символами на входе, которые могут создавать границы слов, являются -+.. Это означает, что если мы находим границу, за которой следует буква, у нас есть неявное значение, 1которое мы добавляем при замене. $&это синоним для $0.

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

Большое спасибо Мартину за этот, этот добавляет к неявной 0для реальной части, если он отсутствовал во входных данных. Мы уверены, что не можем найти число, за которым следует знак плюс или минус или конец строки. Все комплексные числа будут иметь после них букву.

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

Следующие 3 этапа практически одинаковы, за исключением того, на какую букву они влияют. Все они смотрят, чтобы увидеть, не можем ли мы сопоставить букву, и если мы не можем, мы добавим 0термин для него. Единственная причина, по iкоторой +перед этим стоит дополнительная функция, состоит в том, чтобы предотвратить невозможность прочтения действительного значения с помощью iкоэффициента s, все остальные числа разделены их комплексной переменной.

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

Ах, самое интересное. При этом используется новый этап сортировки, обозначаемый обратным символом Oразделителя перед параметром. Хитрость здесь заключается в том, чтобы получить целое число, за которым необязательно следует символ слова, который в этом случае будет совпадать только с одним из символов ijk. Другая используемая опция - то, $что заставляет значение, используемое для сортировки этих совпадений, быть заменой. Здесь мы просто используем необязательную букву, оставленную в качестве значения сортировки. Поскольку Retina сортирует лексикографически по умолчанию, значения сортируются так, как если бы они были в словаре, что означает, что мы получаем соответствия по "", "i", "j", "k"порядку.

-
+-

Эта стадия ставит +знак перед всеми знаками минус, это необходимо, если iпозже мы получим отрицательное значение для на стадии разделения.

^ \ +

Мы убираем ведущие, +чтобы убедиться, что у нас нет лишних ведущих символов новой строки.

S`[ijk+]+

Разделите оставшиеся строки на серии сложных переменных или знак плюс. Это хорошо дает нам одно значение на строку.

FryAmTheEggman
источник
3

Perl 5, 125 байт

#!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 К сожалению, предложенное вами изменение не соответствует вашим ожиданиям. Я пробовал это прежде, чем отправил свой ответ. ;-)
Крис Шестер-Янг
@KennyLau Относительно этого предложенного изменения , Perl \aсоответствует «тревоге», а не алфавиту. Есть \wдля слова-символа (буквенно-цифровой и подчеркивание), но это не сработает здесь; нам нужно, чтобы это не совпадало по числу.
Chris Jester-Young
3
@KennyLau Кстати, у вас достаточно представителя, чтобы поговорить в чате . Не стесняйтесь обсуждать идеи там, вместо того, чтобы постоянно отклонять ваши предложения по редактированию. ;-)
Крис Шестер-Янг
У меня также есть достаточно представителей, чтобы комментировать сейчас. Разве в Perl нет шаблона для [az]?
Утренняя монахиня
1
@KennyLau Не в моих силах.
Крис Шестер-Янг
3

Lua , 185 187 195 183 166 байт ( попробуйте онлайн ) [используется регулярное выражение]

Спасибо @Chris Jester-Young за улучшенное регулярное выражение.

Спасибо @Katenkyo за сокращение до 166 байтов.

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
Привет, Кенни, спасибо за решение. Обычно мы не разрешаем ввод, начинающийся с переменной (как nв этом случае), поэтому вы должны добавить код для чтения ввода.
Исаак
Вы должны быть в состоянии сохранить некоторый байт, изменив ввод с STDIN на аргумент вместо io.read()использования (...). Он будет указывать на первый аргумент командной строки и позволит вам сохранить еще 4 байта :)
Katenkyo
1
Кроме того, запрашиваемый вывод может быть любым, если он может быть истолкован людьми как список, так что вы можете удалить дополнительное форматирование. Включая еще несколько пробелов, которые вы можете сбрить, ваш код может уменьшиться до 166 байт ->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 байт

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

(Для значений, таких как -0 или -0.0, знак минус также печатается в выходных данных, но, поскольку задача заявляет, что «вы можете преобразовать числа в другую форму после анализа, если хотите», и если -0 появляется во входных данных, из этого следует, что это также приемлемо в выводе. @GamrCorps теперь уточнил, что это нормально.)

mIllIbyte
источник
3

JavaScript (ES6), 103 100 байт

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

Изменить: 3 байта сохранены путем переключения с parseIntна charCodeAt, что удобно просто &3получить мне правильный индекс массива.

Нил
источник
Отличная идея parseInt + мод. Думая о базе и префиксе
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))

Тест

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
источник
0

PowerShell, 178 байт

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

Неуправляемый с объяснением

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

Не супер впечатлен, но, тем не менее, это результат.

Matt
источник
0

PHP, 179 байт

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

Попробуйте набор тестов .

nickb
источник
0

Python 3,5 - 496 байт [с использованием регулярных выражений]:

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

Это может быть долго, но в мою защиту, он прекрасно работает, выполняя то, что хочет OP, так как все приведенные тесты были успешными с использованием моего кода.

Неуправляемая версия с пояснениями:

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

Если вышеприведенный текст слишком сложен для чтения, в основном происходит следующее:

  1. Если они есть, все знаки + или - НЕ, за которыми следует число, заменяются на «+1» / «- 1» соответственно.

  2. lambdaФункция определена, которая, при использовании вsorted функции в качестве ключа, сортирует список согласно положить весь номер первый, а затем приказывать остальное в увеличении письма значения ( «я», а затем «J», то «к» в этом случае).

  3. В кватернионе, который теперь заменяет все знаки +/- на 1, если это необходимо, выполняется поиск с использованием регулярных выражений всех букв, которым НЕ предшествует хотя бы одно число, и те буквы, которые совпадают, заменяются на «+1», за которым следует это письмо.

  4. Затем оператор «if» заменяет ВСЕ знаки +/- пробелом, а затем модифицированный кватернион теперь «разделяется» на эти пробелы и возвращается в списке. Затем список сортируется в соответствии с лямбда-функцией, описанной ранее. Наконец, первый элемент в этом списке проверяется, чтобы убедиться, что это число, поскольку оно должно быть, а если нет, то в кватернионе добавляется «+0».

  5. Второй цикл for находит ВСЕ буквы НЕ в кватернионе, находя симметричное различие между набором этих букв, найденных в выражении, и набором, включающим все требуемые буквы. Если они найдены, то в кватернионе добавляется «+0», за которым следует пропущенная буква и пробел.

  6. Наконец, на этом последнем шаге между каждым символом добавляется «,», за которым следует символ +/-, а затем кватернион разделяется на эти пробелы, а затем возвращаемый список сортируется в последний раз в соответствии с лямбда-функция, определенная как «q» ранее. Запятые в выражении отдельно каждую часть кватернион ( в противном случае, вы бы получить что - то подобное 14i+5j+6kс 4i+5j+6k+1). Наконец, этот теперь отсортированный список объединяется в строку, и только числа любого типа (из регулярных выражений) извлекаются и, наконец, возвращаются в списке в правильном порядке, каждый раз.

Р. Кап
источник