Проаналізуйте список підписаних одинарних номерів


16

Унарні числа зазвичай представляють лише неотримані цілі числа, але ми можемо розширити їх на представлення всіх цілих чисел наступним чином:

  • Позитивне ціле число N представлено у вигляді N 1:5 -> 11111
  • Від'ємне ціле число -N подається у вигляді 0N 1:-5 -> 011111
  • Нуль представлений як 0

Потім ми можемо представити список цих чисел однозначно, якщо використовуватимемо 0як роздільник:

3,-2,0,1
111,011,0,1
111 0 011 0 0 0 1
11100110001

Ваше завдання: візьміть рядок, що представляє такий список підписаних одинакових чисел, і перекладіть його у список десяткових чисел.

Деталі

Ви можете припустити, що вхід - це повний список підписаних одинакових чисел. Зокрема, вашій програмі не доведеться обробляти 1) порожній вхід або 2) вхід, який закінчується роздільником.

Ви можете припустити, що величина кожного числа не буде перевищувати 127. Для мов з максимальним розміром рядків або списків ви можете припустити, що введення та вихід будуть відповідати структурам даних вашої мови, але ваш алгоритм теоретично повинен працювати для списку будь-якого розміру.

Ваша програма чи функція можуть виконувати введення-виведення будь-яким із стандартних способів . Введенням може бути рядок або список символів, односимвольні рядки, цілі числа або булеві символи. Ви можете використовувати будь-які два символи для представлення 1та 0; якщо ви не використовуєте 1і 0, будь ласка, вкажіть, які символи ви використовуєте.

Вихідні дані повинні бути десятковими числами у будь-якому розумному форматі списку (зокрема, має бути якийсь роздільник між числами). Негативні цифри слід позначати знаком мінус, хоча якщо ваша мова має інший формат для від'ємних цілих чисел, я також прийму це. Нуль може бути представлений у висновку як 0або -0.

Тестові справи

1 -> 1
0 -> 0 (or -0, and similarly for the other test cases)
011 -> -2
1101 -> 2,1
1100 -> 2,0
11001 -> 2,-1
110001 -> 2,0,1
11100110001 -> 3,-2,0,1
00000001 -> 0,0,0,-1
01111011111111001111111111111110111111111111111100111111111111111111111110111111111111111111111111111111111111111111 -> -4,8,-15,16,-23,42
01111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 -> -127

2
Нітпік: Оскільки це містить '0's, технічно він не є одинарним. Хороший виклик, хоча!
DJMcMayhem

4
@DJMcMayhem Nitpick to the nitpick: Я технічно ніколи не говорив, що це було неоднаково. Це розширення унарного, яке я називаю "підписаним унарним". ;)
DLosc

@DJMcMayhem IMO, виклик полягає саме в тому, що роздільник ( 0) і префікс негативного знаку ( 0) однакові, хоча це все одно однозначно, оскільки ви не можете мати негативні знаки в середині числа (це 182--693-1число? Ні, і жодна не 1111011000101111з точно тієї ж причини).
Ерік Аутгольфер

Чи добре, якщо виведений список знаходиться в зворотному порядку введення?
DJMcMayhem

технічно добре десятковий не є десятковою, оскільки він використовує символ '-'
Unlambder

Відповіді:


10

Python 2 , 73 70 байт

Функція, яка приймає рядок як вхідний і повертає рядкове представлення списку Python. Нуль може бути представлений як 0і -0(коли він останній):

lambda s:`map(len,s.split('0'))`.replace('0, ','-').replace('--','0,')

Пояснення

  1. splitвхідний рядок sна нулях.
  2. Візьміть довжину кожного рядка в отриманому списку (використовуючи map).

Це веде нас довгий шлях. Зрештою, нулі були роздільниками. А цифри були одинарні, тому lenзручно перетворювати їх у десяткові. Але тепер ми переплутали всі використання неділітеля 0. На щастя, усі нероздільні пристрої були провідними нулями, тому вони прийшли після роздільника-нуля і дали нам рядки нульової довжини ( '00'.split('0') == ['', '', '']). Ці рядки нульової довжини потім також стали 0через len.

  1. Перетворіть список у рядок ( використовуючи "зворотні лапки" ), щоб ми могли легше виправити безлад.
  2. replaceкожен нуль, який передує іншому номеру замість цього негативного знака. Це фіксує використання 0знака, але воно порушує буквальні нулі. Буквальним нулям також передував роздільник, тому вони тепер стали парами додаткових тире на наступне число.
  3. replaceкожен --назад в 0елемент у "списку".

1
Ласкаво просимо до PPCG!
Steadybox

Це дійсно творчий підхід! Ви можете додати коротке пояснення, щоб ті, хто не розмовляє Python, також могли оцінити вашу відповідь.
DLosc

@DLosc, дякую, я не знав про те, Також додано пояснення Wordy.
меркатор

8

Сітківка , 23 21 байт

(.)0
$1 
01
-1
1+
$.&

Спробуйте в Інтернеті!

Перший етап (.)0<newline>$1<space>відповідає будь-якому персонажу, за яким слід a 0. Матч замінюється першим символом, після якого пробілом. Це розділяє рядок на окремі числа.

Другий етап 01<newline>-1замінює 0's перед блоком 1' s на -знак.

Останній етап 1+<newline>$.&відповідає всім блокам 1s і замінює їх довжиною групи.

Ось приклад з виведенням окремих етапів.


Дуже приємно - всі мої ідеї здаються на 24 байти ...
Ніл

1
Не могли б ви додати пояснення? Я не розмовляю з Retina.
Даніель

@Dopapp додав пояснення
пт

7

Vim, 56 байт

:s/\v(0?1*)0?/\1\r/g|%s/0/-/|%s/1*$/\=len(submatch(0))
D

Спробуйте в Інтернеті!

Я не публікував у vim деякий час. Я в основному використовую vim, оскільки V іноді болить. Оскільки countкоманда, яка ідеально підходить для отримання числа '1 у рядку, замінить будь-які' 0 у рядку, тому ми не зможемо відмовити її згодом.

Пояснення:

Це на один байт коротший від прямого:

:s/\v(0?1*)0?/\1\r/g
:%s/0/-
:%s/1*$/\=len(submatch(0))
D

завдяки командному ланцюгу. Оскільки цей розділяє команди, я буду використовувати його для пояснення.

:s/                     " Substitute
                        " Search for...
   \v                   "   Enable 'magic'. This determines whether certain atoms require a backslash or not.
                        "   Without it we would have: '\(0\?1*\)0\?', which is 2 bytes longer
      0?                "   An optional 0
        1*              "   Followed by any number of '1's
     (    )             "   (call that group 1)
           0?           "   Followed by another optional 0
             /          " Replace it with...
              \1        "   Subgroup 1
                \r      "   A newline
                  /g    " Do this for every match on the current line.

Тепер кожен підписаний одинаковий номер знаходиться в окремому рядку. Використовуючи "11100110001" як приклад, у цей момент ми матимемо:

111
011
0
1

:%s/0   " Replace every 0
     /- " With a dash  

:%s/1*$/                    " Replace every run of 1's at the end of a line
        \=len(submatch(0))  " With the length of said run

Оскільки ми додавали нові рядки в кінці кожного матчу, у нас був порожній рядок перед його запуском. Після запуску цього у нас буде "0" (оскільки він збігається з пробігом 0 '1'). Тому ми просто закликаємо Dвидалити цей рядок, залишивши його порожнім


Тьфу. :%s/1+$/ви отримаєте на один байт коротше, якби не потреба в зворотному +
нахилі

@NieDzejkob Я не розумію, чому це було б коротше. А також, що дасть -замість 0або-0
DJMcMayhem

Я хотів таким чином усунути останній рядок: P, неважливо.
NieDzejkob

7

Haskell , 68 66 байт

f(x:r)|(a,b)<-span(>0)r=([(0-),(1+)]!!x$sum a):[z|_:t<-[b],z<-f t]

Спробуйте в Інтернеті! Вводиться як список нулів та одиниць. Приклад використання: f [0,0,0,1,1]врожайність [0,-2].

Пояснення:

Зрівняння шаблону у f(x:r)|(a,b)<-span(>0)rприв'язціx з першим елементом вводу, aсписком (потенційно порожнім) наступних 1s та bрештою введення. Отримавши на вході [0,1,1,1,0,0,1], ми отримуємо x=0, a=[1,1,1]і b=[0,0,1].

Поточне число тоді є або сумою a заперечення if x=0, або сумою aплюс один, якщо x=1. Це досягається шляхом індексації з xв список , що містить заперечення і приріст функції, і застосовуючи отриману функцію до суми a: [(0-),(1+)]!!x$sum a.

Список відпочинку bабо порожній, або містить роздільний нуль та наступне число. Розуміння списку [z|_:t<-[b],z<-f t]намагається відповідати bшаблону _:t, тобто забути головний елемент і прив’язати решту списку до t. Якщо bпорожній цей збіг не вдається, і розуміння списку оцінюється [], що є базовим випадком для рекурсії. В іншому випадку функція fрекурсивно застосовується доt і розуміння списку оцінюється для всіх елементів zз результату f t.


3

Мова Вольфрама (Mathematica) , 80 байт

StringCases[#<>"0",x_~~Shortest@y___~~"0":>(If[x=="0",-#,#+1]&)@StringLength@y]&

Спробуйте в Інтернеті!

Зловживає механік с StringCases , оскільки він не перевіряє шаблони, що перекриваються. Оскільки ми шукаємо зліва направо, без перекриттів, ми завжди отримуємо лише цілі числа, які нам потрібні.

Пояснення

#<>"0"

Додайте нуль в кінці

StringCases

Знайти всі наведені нижче моделі ...

x_~~Shortest@y___~~"0"

Одиничний символ (зателефонуйте x), за яким слідує найкоротший можливий нульовий чи довший рядок (викликайте його y), а за ним - нуль.

(If[x=="0",-#,#+1]&)@StringLength@y

Застосувати до відповідного шаблону: візьміть довжину y. Якщо xнуль, то заперечуйте значення. Інше, приріст один.

Це також охоплює 00, оскільки yце був би порожній рядок, і ми би обчислили -0( == 0).


3

Мозг-Флак , 94 (70?) Байт

([]){{}({(<()>)()}{}(<>)<>{<([{}]<>{})><>})
{{}<>([{}])(<>)}{}{}([])}{}<>([]){{}({}<>)<>([])}<>

Спробуйте в Інтернеті!

Це насправді напрочуд лаконічно для мозку.

Ось коментована / читаема версія:

([])

{

    #Pop the Stack height
    {}

    (
        #If there isn't a leading 0, evaluate to 1...
        {
            (<()>)

            ()
        }

        #Pop the 0
        {}

        #Push a 0 onto the alternate stack
        (<>)
        <>

        #Run of '1's
        {
            #Decrement the alternate stack
            <([{}]<>{})>
            <>
        }

        #And push it here
    )

    #Was there a not leading 0?

    {
        {}

        #Invert the value on the alternate stack
        <>([{}])(<>)
    }

    #Pop 2 zeros
    {}{}


    ([])

}{}<>

#Push stack height
([])

#Reverse the stack
{

    {}

    ({}<>)

    <>([])

}<>

Якщо вихід може бути зворотним, ми можемо зробити це замість 70:

([]){{}({(<()>)()}{}(<>)<>{<([{}]<>{})><>}){{}<>([{}])(<>)}{}{}([])}<>

Цей наконечник шахти є майже ідеально підходить для цієї ситуації. Але це не зовсім спрацьовує, оскільки нам доводиться натискати 0, перш ніж робити операцію (рахуючи '1'), і операція відбувається в циклі. Найкоротший, який я міг би придумати, скориставшись цією порадою, це:

([]){{}({(<()>)()}{}(<>)<>{<([{}]<>{})><>})
{{}<>([{}])(<>)}{}{}(<>())<>([])}{}<>{{}({}<>)<>}<>

що також 94 байти.



3

Лушпиння , 20 18 17 15 14 байт

Γ~:?Σṁ_Πȯ₀tΣġ/

Спробуйте в Інтернеті!

Пояснення

Γ~:?Σṁ_Πȯ₀tΣġ/  Input is a list, say x = [0,1,1,0,0,0,1,1]
            ġ   Group by
             /  division.
                This splits x right before each 0: [[0,1,1],[0],[0],[0,1,1]]
Γ               Deconstruct into head y = [0,1,1] and tail z = [[0],[0],[0,1,1]]
   ?Σṁ_Π        Apply to y:
       Π         Product: 0
   ?Σ            If that is nonzero, take sum of y,
     ṁ_          else take sum of negated elements of y: u = -2
        ȯ₀tΣ    Apply to z:
           Σ     Concatenate: [0,0,0,1,1]
          t      Drop first element: [0,0,1,1]
         ₀       Recurse: [0,2]
 ~:             Tack u to the front: [-2,0,2]

Розщеплення працює так. ġ/розбиває свій аргумент між кожною парою елементів, a,bдля яких /a bє помилковим. /a bце поділ з перевернутими аргументами, так bрозділеними на a. Відповідні значення цієї програми:

  • /1 1дає 1(правда).
  • /1 0дає 0(помилковий).
  • /0 1дає Inf(позитивна нескінченність, правда).
  • /0 0дає Any(особливе NaN-подібне значення, хибність).

3

Acc !! , 252 237 байт

N
Count i while _/48 {
Count n while 48/_ {
Write 45
50+N
}
_+49/_*50
Count u while _%50/49 {
_+100-_%50+N
}
_/50-1
Count h while _/200 {
Write _/200+48
_%200+1
}
Count t while _/20+_%2 {
Write _/20+48
_%20-_%2
}
Write _/2+48
Write 9
N
}

Використання -0. Виводить цифри, розділені символами вкладки, з вкладкою в кінці. Спробуйте в Інтернеті!

Час написання фактичного алгоритму: 20 хвилин. Час налагодження мого десяткового вихідного коду: 45 хвилин. : ^ П

З коментарями

Я не знаю, чи ці коментарі дуже добре пояснюють код - вони ґрунтуються на моїх замітках до себе, поки я його писав, тому вони припускають деяке розуміння того, як Acc !! працює. Якщо щось потребує більше пояснень, дайте мені знати, і я спробую зробити це більш зрозумілим.

# We partition the accumulator _ as [number][flag][nextchar]
# [flag] is a 2-value slot and [nextchar] a 50-value slot
# So [nextchar] is _%50, [flag] is _/50%2, [number] is _/100
# [flag] is 1 if we're in the middle of reading a number, 0 if we're between numbers
# It is also used for outputting as decimal (see below)
# Possible input characters are 0, 1, and newline, so [nextchar] is 48, 49, or 10

# Read the first character
N
# Loop while the character we just read is 0 or 1 and not newline
Count i while _/48 {
  # What we do in the loop depends on the combination of [flag] and [nextchar]:
  # 0,48 (start of number, read 0) => write minus sign, [flag] = 1, read another char
  # _,49 (read 1) => increment [number], [flag] = 1, read another char
  # 1,48 (middle of number, read 0) => write/clear [number], status = 0, read another
  #      char
  # 1,10 (middle of number, read <cr>) => ditto; the next read will be 0 for eof, which
  #      means the acc will be less than 48 and exit the loop

  # Process leading 0, if any
  Count n while 48/_ {
    # acc is 48: i.e. [number] is 0, [flag] is 0, [nextchar] is 48 (representing a 0)
    # Output minus sign
    Write 45
    # Set [flag] to 1 (thereby exiting loop) and read [nextchar]
    50+N
  }
  # If number starts with 1, then we didn't do the previous loop and [flag] is not set
  # In this case, acc is 49, so we add (50 if acc <= 49) to set [flag]
  _+49/_*50

  # Process a run of 1's
  Count u while _%50/49 {
    # [nextchar] is 49 (representing a 1)
    # Increment [number] and read another
    _+100-_%50+N
  }

  # At this stage, we know that we're at the end of a number, so write it as decimal
  # This is "easier" (ha) because the number has at most three digits
  # We shift our partitioning to [number][flag] and set [flag] to 0
  _/50-1

  # Output hundreds digit if nonzero
  # Since [number] is _/2, the hundreds digit is _/200
  Count h while _/200 {
    Write _/200+48
    # Mod 200 leaves only tens and units; also, set [flag] to 1
    _%200+1
  }
  # Output tens digit (_/20) if nonzero OR if there was a hundreds digit
  # In the latter case, [flag] is 1
  Count t while _/20+_%2 {
    Write _/20+48
    # Mod 20 leaves only units; clear [flag] if it was set
    _%20-_%2
  }
  # Write units unconditionally
  Write _/2+48

  # Write a tab for the separator
  Write 9
  # Read another character
  N
}



2

Желе ,  19  18 байт

Має бути кращий спосіб ...

®ḢN$Ḣ©?ṄEȧ
ṣ0L€ÇL¿

Повна програма, що друкує кожен номер, після чого надсилається лінійка.

Спробуйте в Інтернеті!

Як?

®ḢN$Ḣ©?ṄEȧ - Link 1, print first number and yield next input: list of numbers, X
           -                              e.g. [8,0,15,16,...] or [0,4,8,0,15,16,...]
      ?    - if...
    Ḣ      - condition: yield head and modify  8([0,15,16,...])   0([4,8,0,15,16,...])  
     ©     -            (copy to register)     8                  0
®          - then: recall from the register    8
   $       - else: last two links as a monad:
 Ḣ         -         yield head and modify                        4([8,0,15,16,...])
  N                  negate                                      -4
       Ṅ   - print that and yield it           8                 -4
        E  - all equal (to get 0 to be truthy) 1                  1
         ȧ - AND the (modified) input          [0,15,16,...]      [8,0,15,16,...]
           -   (ready to be the input for the next call to this link)

ṣ0L€ÇL¿ - Main link: list e.g. [0,1,0,0,0,0,1,1]
ṣ0      - split at zeros       [[],[1],[],[],[],[1,1]
  L€    - length of €ach       [0,1,0,0,0,2]
      ¿ - while...
     L  - condition: length                           1  1  1  0  ([0,1,0,0,0,2], [0,0,0,2], [0,2], [])
    Ç   - action: call the last link (1) as a monad  -1  0 -2     ( - 1            - 0        - 2)

1

QBasic, 88 86 байт

1u$=INPUT$(1)
z=u$<"1
IF n*z THEN?(1-2*s)*(n-s):s=0:n=0ELSE s=s-z:n=n+1
IF"!"<u$GOTO 1

Це було весело. Багаторазова редакція, починаючи з 107-байтової версії, призвела до одного з найбільш затуманених бітів QBasic, я думаю, що я коли-небудь писав.(Редагувати: Як не дивно, я зміг переграти 2 байти, зробивши код яснішим.)

Примітка: ця програма читає введення користувачем одного символу одночасно, не повторюючи його на екрані (результат використання INPUT$(1)замість звичайного INPUTоператора). Отже, під час введення тексту ви не побачите 1 і 0, але десяткові числа з’являться під час їх обчислення. Не забудьте натиснути Enterв кінці введення, щоб побачити останнє число та завершити програму.

Безгольова версія

sign = 0
num = 0
DO
  digit$ = INPUT$(1)
  isZero = (digit$ < "1")
  IF num > 0 AND isZero THEN
    PRINT (1 - 2 * sign) * (num - sign)
    sign = 0
    num = 0
  ELSE
    IF isZero THEN sign = 1
    num = num + 1
  END IF
LOOP WHILE "!" < digit$

Пояснення

(AKA "Що ?? це все ще не має сенсу!")

Основна стратегія полягає у запуску циклу, який INPUT$(1)кожен раз перехоплює один символ , робить його і продовжує циклічно до тих пір, поки у символу значення ASCII більше, ніж у !(тобто не був новий рядок).

Ми відстежуємо кількість незавершених чисел за допомогою двох змінних. num- кількість символів у поточному підписаному одинарному номері (включаючи будь-який провідний нуль). signє, 1якщо число мало провідний нуль, 0якщо ні. І 0те і інше потрібно ініціалізувати , що відмінно підходить для версії для гольфу, оскільки числові змінні в QBasic автоматично ініціалізуються на 0.

Щоразу, коли ми читаємо персонаж, перше, що потрібно, визначити, чи це він, 1чи 0. Ми будемо використовувати цей результат двічі, тому ми зберігаємо його isZero. Технічно це ім'я вводить в оману, оскільки значення також буде трибуним, якщо символ є новим рядком. Зауважте, що в QBasic -1truthy є, а фальси є 0.

Тепер, якщо ми перебуваємо в середині читання числа ( num > 0) і натискаємо нуль або кінець введення ( isZero), нам потрібно обчислити, яке число ми закінчили читати.

  • signмагазини 0для позитивного, 1для негативного. Щоб отримати 1позитив і -1негатив, нам потрібно 1-2*sign.
  • numзберігає правильну величину для позитивів, але на одну більше, ніж величину для негативів (оскільки вона включає маркер знака). Таким чином, ми можемо використовувати num-signдля величини.

Помножте їх разом і роздрукуйте; потім скинути signі numдо 0підготовки до читання наступного номера.

В іншому випадку (якщо ми не досягли нуля, або якщо на початку числа ми досягли нуля), ми оновлюємо signта виконайте numнаступне:

  • signстає, 1якщо ми дивимось на провідний нуль; інакше, якщо ми дивимось на одну, вона залишається на тому, що вже було. Код для гольфу - s=s-zце те саме:
    • Якщо це провідний нуль, zє -1. Оскільки sгарантовано буде 0(бо це початок нового номера), s-zбуде 1.
    • Якщо це один, zє 0. Потім s-zзалишається за будь-якою цінністю sраніше.
  • num збільшується.

Це воно!


0

JavaScript (ES6), 60 байт

Повертає розділений пробілом список цілих чисел.

s=>(0+s).replace(/00?1*/g,s=>(l=s.length,+s[1]?l-1:2-l)+' ')

Тестові справи


Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.