解析带符号的一元数列表


16

一元数通常只表示非负整数,但是我们可以如下扩展它们以表示所有整数:

  • 正整数N表示为N 15 -> 11111
  • 负整数-N表示为0N,后跟N 1-5 -> 011111
  • 零表示为 0

如果使用0分隔符,则可以明确表示这些数字的列表:

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

您的任务:获取一个表示这样的带符号的一进制数字列表的字符串,并将其转换为十进制数字列表。

细节

您可以假定输入是带符号的一进制数字的完整列表。特别是,您的程序将不必处理1)空输入或2)以分隔符结尾的输入。

您可以假设每个数字的大小不超过127。对于具有最大字符串或列表大小的语言,可以假设输入和输出将适合您语言的数据结构,但是从理论上讲,算法应适用于以下列表任何大小。

你的程序或功能可以执行的I / O任何标准的方法。输入可以是字符串或字符列表,单字符字符串,整数或布尔值。您可以使用任何两个字符来表示10; 如果您不使用10,请指定您正在使用的字符。

输出必须是任何合理列表格式的十进制数字(尤其是数字之间必须有某种分隔符)。负数应以减号表示,尽管如果您的语言对负整数使用不同的格式,我也会接受。零可以在输出中表示为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
Nitpick:由于包含'0's,因此从技术上讲它不是一元的。好挑战!
DJMcMayhem

4
@DJMcMayhem Nitpick对nitpick:从技术上讲,我从来没有说过它是一元的。它是一扩展,我称之为“签名一元”。;)
DLosc

@DJMcMayhem IMO,挑战尤其是分隔符(0)和负号前缀(0)是相同的,尽管它仍然是明确的,因为您不能在数字中间使用负号(是182--693-1数字吗?也不是1111011000101111出于完全相同的原因)。
Erik the Outgolfer '17

如果输出列表与输入顺序相反,可以吗?
DJMcMayhem

以及技术上小数点是不是小数要么因为它使用了“ - ”符号
Unlambder

Answers:


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,谢谢,我不知道反引号。还添加了罗y的解释。
墨卡托

8

视网膜23 21字节

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

在线尝试!

第一阶段(.)0<newline>$1<space>匹配任何字符,后跟一个0。匹配将替换为第一个字符,后跟一个空格。这会将字符串分割成单个数字。

第二阶段在符号的的之前01<newline>-1将的替换为。01-

最后一个阶段1+<newline>$.&匹配1并将其替换为组的长度。

是各个阶段的输出示例。


非常好-我的所有想法似乎都以24字节
Neil

1
你能补充一个解释吗?我不会说视网膜。
丹尼尔(Daniel)

@Dopapp添加了解释
ovs

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

@NieDzejkob我不明白为什么会这么短。而且,这将-代替0-0
DJMcMayhem

我想以这种方式消除最后一行:P,没关系。
NieDzejkob

7

Haskell68 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]yields [0,-2]

说明:

模式匹配in f(x:r)|(a,b)<-span(>0)r绑定x到输入的第一个元素a,后继1s 的(可能为空)列表以及b输入的其余部分。给定输入[0,1,1,1,0,0,1],我们得到x=0a=[1,1,1]b=[0,0,1]

那么,当前数要么是a否定的if 的总和,要么是if 加上1 x=0的总和。这是通过用索引到包含求反和增量函数的列表中并将结果函数应用于的总和来实现的ax=1xa[(0-),(1+)]!!x$sum a

其余列表b为空,或者包含零和下一个数字。列表理解[z|_:t<-[b],z<-f t]尝试匹配b模式_:t,就是忘记head元素并将列表的其余部分绑定到t。如果b为空,则此匹配失败,列表推导的计算结果为[],这是递归的基本情况。否则,该函数f将递归应用于,t列表理解将z根据的结果对所有元素求值f t


3

Wolfram语言(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

Brain-Flak,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是falsy。 /a b是具有倒置参数的除法,因此b除以a。该程序中的相关值为:

  • /1 1给出1(真实)。
  • /1 00(虚假)。
  • /0 1给出Inf(正无穷大,真实)。
  • /0 0给出Any(特殊的类似于NaN的值,虚假)。

3

加速! 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分钟。:^ P

有评论

我不知道这些注释是否很好地解释了代码-它们是基于我在编写代码时对自己的注释,因此它们假定了对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$

说明

(又名“什么?那仍然没有道理!”)

基本策略是运行一个循环,INPUT$(1)每次捕获一个字符,对其进行填充,并在该字符的ASCII值大于ASCII值的情况下保持循环。!(即不是换行符)的情况下。

我们使用两个变量来跟踪进行中的数字。num是当前带符号的一进制数字(包括任何前导零)中的字符数。sign1数字是否有前导零,0如果没有。这两个都需要初始化为0,这对于高尔夫球版本非常有用,因为QBasic中的数字变量会自动初始化为0

每当我们读一个字符时,第一件事就是确定它是1还是0。我们将使用此结果两次,因此将其存储在中isZero。从技术上讲,该名称具有误导性,因为如果字符是换行符,则该值也将是真实的。请注意,QBasic中的-1true是,false是0

现在,如果我们正处于读取数字(num > 0)的过程中,并且输入为零或输入结尾(isZero),则需要计算已完成读取的数字。

  • sign存储0为正,1为负。为了获得1积极和-1消极,我们需要1-2*sign
  • num存储正值的正确幅度,但比负值的幅度大一倍(因为它包含符号标记)。因此我们可以使用num-sign幅度。

将它们相乘并打印;然后复位sign,并num0以准备读取下一个号码。

否则(如果我们未达到零,或者在数字开头未达到零),我们将进行更新signnum如下所示:

  • 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)+' ')

测试用例


0

Lua,58个字节

(...):gsub("(0?)(1*)0?",function(s,n)print(#n-2*#s*#n)end)

在线尝试!

完整程序,从命令行获取输入,然后将数字打印到标准输出,并用换行符分隔。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.