2048类数组移位


80

假设我们要像2048游戏中那样移动数组:如果数组中有两个相等的连续元素,请将它们合并为value元素的两倍。Shift必须返回一个新的数组,其中每对连续的相等元素都用它们的和替换,并且对不应该相交。移位仅执行一次,因此我们不需要再次合并结果值。请注意,如果我们有连续3个相等的元素,我们总结最右边的人,因此,例如,[2, 2, 2]应该成为[2, 4][4, 2]

任务是编写最短函数,该函数接受一个数组并返回移位的数组。

您可以假设所有整数都将严格为正。

例子:

[] -> []
[2, 2, 4, 4] -> [4, 8]
[2, 2, 2, 4, 4, 8] -> [2, 4, 8, 8]
[2, 2, 2, 2] -> [4, 4]
[4, 4, 2, 8, 8, 2] -> [8, 2, 16, 2]
[1024, 1024, 512, 512, 256, 256] -> [2048, 1024, 512]
[3, 3, 3, 1, 1, 7, 5, 5, 5, 5] -> [3, 6, 2, 7, 10, 10]

我对使用reduce的解决方案也很感兴趣:)


11
这是一个非常好的第一个挑战。欢迎光临本站!
DJMcMayhem

1
输入不一定要排序并且数字大于零,这是对数字的唯一限制。我认为我们可以让最大值适合标准的int32范围。空数组给出的结果是空数组。感谢您的参与,感谢您:)
greenwolf

3
对于仍投票决定不清楚的人来说,挑战本质上可以归结为:假设您有一个正整数数组。从头到尾遍历整个过程。如果当前元素等于下一个元素,则用两者之和替换,并在替换移至该元素,然后对该元素和下一个元素再次执行此检查。重复直到到达数组的开头。
user2428118 '16

1
@Titus“请注意,如果我们有3个连续的相等元素,则必须对最右边的元素求和,例如,[2,2,2]应该变成[2,4],而不是[4,2]。”
Martin Ender

1
空数组的裁定是不幸的。它使一些答案无效,包括我自己的答案。
丹尼斯

Answers:



19

Haskell,47 57 50字节

e#l|a:b<-l,e==a= -2*a:b|1<2=e:l
map abs.foldr(#)[]

用途reduce(或fold在Haskell中称为,这里是右对齐foldr)。用法示例:map abs.foldr(#)[] $ [2,2,2,4,4,8]-> [2,4,8,8]

编辑:+10字节以使其也可用于未排序的数组。合并的数字作为负值插入,以防止第二次合并。他们被决赛纠正 map abs


带底片的技巧真是太好了!
xnor

14

脑高射炮158 96

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

在线尝试!

说明:

1反转列表(将所有内容移至另一个堆栈,但这无关紧要)

{({}<>)<>}<>
{        }   #keep moving numbers until you hit the 0s from an empty stack
 ({}<>)      #pop a number and push it on the other stack
       <>    #go back to the original stack
          <> #after everything has moved, switch stacks

2执行步骤3-6,直到此堆栈上没有剩余的东西为止:

{                                                                                         }

3复制前两个元素(2 3-> 2 3 2 3)

(({}<>)<><(({})<<>({}<>)>)>)

(({}<>)<>                   #put the top number on the other stack and back on the very top
         <(({})             #put the next number on top after:
               <<>({}<>)>   #copying the original top number back to the first stack
                         )>)

4如果顶部的两个相等,则在顶部放一个1,否则,则放一个0(来自Wiki)

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

5如果前两个相等(在顶部不为零),则添加下两个并推结果

{{}(<({}{})>)}{}
{            }   #skip this if there is a 0 on top
 {}              #pop the 1
   (<      >)    #push a 0 after:
     ({}{})      #pop 2 numbers, add them together and push them back on 
              {} #pop off the 0

6将顶部元素移至另一个堆栈

({}<>)<>

7切换到另一个堆栈并隐式打印

<>

请在语言名称后添加逗号,否则会破坏排行榜ty:P
仅限ASCII

9

PHP,116字节

<?$r=[];for($c=count($a=$_GET[a]);$c-=$x;)array_unshift($r,(1+($x=$a[--$c]==$a[$c-1]))*$a[$c]);echo json_encode($r);

要么

<?$r=[];for($c=count($a=$_GET[a]);$c--;)$r[]=$a[$c]==$a[$c-1]?2*$a[$c--]:$a[$c];echo json_encode(array_reverse($r));

-4字节(如果输出可以是数组print_r而不是'json_encode`)

176个字节用正则表达式解决

echo preg_replace_callback("#(\d+)(,\\1)+#",function($m){if(($c=substr_count($m[0],$m[1]))%2)$r=$m[1];$r.=str_repeat(",".$m[1]*2,$c/2);return trim($r,",");},join(",",$_GET[a]));

1
您不能使用排序,因为结果并不总是排序:[4、4、2、8、8、2]-> [
8、2、16、2

@Crypto您已经添加了新的测试用例。之前使用排序是好的
约尔格Hülsermann

for($i=count($a=$argv);--$i;)$b[]=($a[$i]==$a[$i-1])?2*$a[$i--]:$a[$i];print_r(array_reverse($b));同样的想法,但更短
加密

@Crypto我不确定输出是字符串表示形式还是数组。对于测试用例[],我需要$r=[];感谢你的帮助
约尔格Hülsermann


8

视网膜 32

\d+
$*
r`\b\1 (1+)\b
$1$1
1+
$.&

r第3行上的激活从右到左的正则表达式匹配。这意味着\1引用需要先于引用的(1+)捕获组。

在线尝试。


很好..从右到左的匹配选项非常方便!它是.NET正则表达式的一部分还是Retina功能的一部分?
达达

我刚好要在26 发布我的文章,使用换行分隔符作为输入格式:retina.tryitonline.net / ...主要的节省来自于此,并使用音译来摆脱第二个替代。
马丁·恩德

@Dada这是.NET的功能(它在引擎盖下用于启用任意长度的lookbehinds)。视网膜还没有独特的正则表达式功能(尽管它具有一些独特的替代功能)。
马丁·恩德

1
@MartinEnder好,谢谢!.NET正则表达式真的很棒!嫉妒的Perl编码器被发现
Dada's

@MartinEnder我,您的解决方案与众不同,足以保证您需要另一个答案
Digital Trauma

8

Perl,41个字节

包括+1 -p

给出STDIN上的输入顺序:

shift2048.pl <<< "2 2 2 4 4 8 2"

shift2048.pl

#!/usr/bin/perl -p
s/.*\K\b(\d+) \1\b/2*$1.A/e&&redo;y/A//d

8

Python,61个字节

def f(l):b=l[-2:-1]==l[-1:];return l and f(l[:~b])+[l[-1]<<b]

布尔值b通过检查对于长度为1或0的列表而言是否安全,来检查后两个元素是否应该合拢。如果最后一个元素后面附加1了等于或2不等于的乘数。它被附加到列表上的递归结果中,其中许多元素被切掉。感谢Dennis提供1个字节!


[l[-1]<<b]保存一个字节。
丹尼斯

l[-2:-1][l[-2]]
mbomb007

2
我需要它来处理大小为0和1的列表
。– xnor

7

Perl,43 +1(-p)= 44字节

Ton Hospel提出了41个字节的答案,请查看!

-4感谢@Ton Hospel!

编辑:添加\b,因为没有它,输入就失败了,就像24 4输出一样28

$_=reverse reverse=~s/(\b\d+) \1\b/$1*2/rge

-pflag 运行:

perl -pe '$_=reverse reverse=~s/(\b\d+) \1\b/$1*2/rge' <<< "2 2 2 4 4"


我没有看到另一种方式比使用reverse两次对折(如只是s/(\d+) \1/$1*2/ge将离开倍,即2 2 2会成为4 2代替2 4)。因此,由于reverse... 丢失了14个字节……我仍然认为必须有另一种(更好的)方式(毕竟是perl!),如果找到了,让我知道!


reverse reverse似乎有点长。我不是Perl的专家,但是有什么方法可以使您成为捷径reverse(如果没有其他问题,请[ab]使用eval)?
Cyoce '16

不错的性爱。请注意,您可以省掉($_)
Ton Hospel'Ton Hospel

@TonHospel谢谢。确实,reverse貌似的文档reverse不能不带参数地调用(示例显示它可以,但是只有一个原型:)reverse LIST,所以我忘了$_成为默认参数;)
Dada

一个LIST可以是空的...
Ton Hospel '16

@TonHospel确实如此,但是通常当操作员将其$_用作默认参数时,文档会指定没有参数(如printlenght...)的原型。也许那只是我的错误印象。
达达

7

JavaScript(ES6),68个字节

f=a=>a.reduceRight((p,c)=>(t=p[0],p.splice(0,c==t,c==t?c+t:c),p),[])
    
console.log([
  [],
  [2, 2, 4, 4],
  [2, 2, 2, 4, 4, 8],
  [2, 2, 2, 2],
  [4, 4, 2, 8, 8, 2],
  [1024, 1024, 512, 512, 256, 256],
  [3, 3, 3, 1, 1, 7, 5, 5, 5, 5],
].map(f))


2
不错,但是根据执行的代码段:[1024, 1024, 512, 512, 256, 256]解析为as [2048, 512, 1024]而不是[2048, 1024, 512]...?
WallyWest '16

7

Perl 5.10的,61 50个字节(49 + 1为标志)

感谢Ton Hospel节省了11个字节!

无正则表达式的解决方案,带有-a标志:

@a=($F[-1]-$b?$b:2*pop@F,@a)while$b=pop@F;say"@a"

试试这里!


不错的替代方法。可怜的数组几乎总是在perl中输给字符串。不过,您可以通过将代码@a=($F[-1]-$b?$b:2*pop@F,@a)while$b=pop@F;say"@a"
传播

@TonHospel实际上,我倾向于避免基于字符串的解决方案(只是为了证明Perl可以做的更多!)。反正我也不会赢球:D感谢高尔夫技巧!
保罗·皮卡德

7

JavaScript(ES6),68 65 58 57 65 64字节

@ l4m2节省了1个字节

现在已解决了未排序数组的问题,因为已经澄清了这种输入是可以预期的。

f=(a,l=[],m)=>(x=a.pop())*!m-l?f(a,x).concat(l):x?f(a,2*x,1):[l]

console.log(f([2, 2, 4, 4]));
console.log(f([2, 2, 2, 4, 4, 8]));
console.log(f([2, 2, 2, 2]));
console.log(f([4, 2, 2]));


1
我将建议您刚刚进行编辑:)
ETHproductions

a=>(a.reverse()+'').replace(/(.),\1/g,(c,i)=>i*2).split`,`.reverse()
l4m2

@ l4m2确实适用于个位数输入,但会失败[1024, 1024, 512, 512, 256, 256](我认为此测试用例可能已在以后添加)。
阿诺尔德

@Arnauld好吧,你的也失败了……
l4m2

f=(a,l=[],m)=>(x=a.pop())*!m-l?f(a,x).concat(l):x?f(a,2*x,1):[l]
l4m2

6

05AB1E,26个字节

D¥__X¸«DgL*ê¥X¸«£vy2ôO})í˜

在线尝试!

广义步骤

  1. 通过减法来减少连续元素的区别
  2. 通过减去这些位置的索引来减少,以找到连续元素的长度
  3. 将输入分成这些长度的块
  4. 将大块成对
  5. 每对加总
  6. 反转每个求和的块
  7. 展平到一维列表

5

Mathematica,53个字节

Join@@(Reverse[Plus@@@#~Partition~UpTo@2]&/@Split@#)&

说明

Split@#

将输入分成由相同元素组成的子列表。即{2, 2, 2, 4, 8, 8}成为{{2, 2, 2}, {4}, {8, 8}}

#~Partition~UpTo@2

将每个子列表划分为最多2个分区,即{{2, 2, 2}, {4}, {8, 8}}成为{{{2, 2}, {2}}, {{4}}, {{8, 8}}}

Plus@@@

每个分区总计。即{{{2, 2}, {2}}, {{4}}, {{8, 8}}}成为{{4, 2}, {4}, {16}}

Reverse

反转结果,因为Mathematica的Partition命令从左到右,但是我们希望分区朝另一个方向。即{{4, 2}, {4}, {16}}成为{{2, 4}, {4}, {16}}

Join@@

展平结果。即{{2, 4}, {4}, {16}}成为{2, 4, 4, 16}


嗨,JHM!感谢您的回答。我对Mathematica的理解不是很好,所以您可以对发生的事情添加一些解释吗?
isaacg

Plus@@@是的Tr/@,我认为您可以避免使用括号,并且Join@@如果您使用##&@@的结果Reverse(虽然尚未测试)。
马丁·恩德

5

Java 7,133字节

Object f(java.util.ArrayList<Long>a){for(int i=a.size();i-->1;)if(a.get(i)==a.get(i-1)){a.remove(i--);a.set(i,a.get(i)*2);}return a;}

输入是一个ArrayList,它只是向后循环,在必要时将其删除并加倍。

Object f(java.util.ArrayList<Long>a){
    for(int i=a.size();i-->1;)
        if(a.get(i)==a.get(i-1)){
            a.remove(i--);
            a.set(i,a.get(i)*2);
        }
    return a;
}

您正在将Long第3行的引用与进行比较==。考虑一下a.get(i)-a.get(i-1)==0
雅各布

4

Perl,37个字节

包括+4 -0n

在STDIN上将输入作为单独的行运行:

perl -M5.010 shift2048.pl
2
2
2
4
4
8
2
^D

shift2048.pl:

#!/usr/bin/perl -0n
s/\b(\d+
)(\1|)$//&&do$0|say$1+$2

4

Haskell,56个字节

g(a:b:r)|a==b=a+b:g r|l<-b:r=a:g l
g x=x
r=reverse
r.g.r

4

PHP,8610099 94字节

for($r=[];$v=+($p=array_pop)($a=&$argv);)array_unshift($r,end($a)-$v?$v:2*$p($a));print_r($r);

需要PHP 7.0;从命令行参数获取值。

运行-nr在线尝试


2
[2,2,2]返回[4,2]而不是[2,4]
加密

for($r=[];$v=($p=array_pop)($a=&$_GET[a]);)array_unshift($r,end($a)-$v?$v:2*$p($a));print_r($r);是1个字节短
约克Hülsermann

3

朱莉娅205字节

t(x)=Val{x}
s(x)=t(x)()
f^::t(1)=f
^{y}(f,::t(y))=x->f(((f^s(y-1))(x)))
g()=[]
g{a}(::t(a))=[a]
g{a}(::t(a),B...)=[a;g(B...)]
g{a}(::t(a),::t(a),B...)=[2a;g(B...)]
K(A)=g(s.(A)...)
H(A)=(K^s(length(A)))(A)

要调用的函数是 H

例如 H([1,2,2,4,8,2,])

这绝不是朱莉娅最简单的方法。但是它是如此的酷,以至于我还是想分享它。

  • t(a) 是一个值类型,表示值(a)。
  • s(a) 是该值类型的实例
  • g是分派差值(使用值类型)和参数编号的功能。那很酷
  • K只是包装g

特别酷的部分:

f^::t(1)=f
^{y}(f,::t(y))=x->f(((f^s(y-1))(x)))

这定义了^适用于功能的操作员。所以这K^s(2)(X)是一样的K(K(X)) 所以H只是叫KK一堆倍-足够的时间肯定折叠任何巢式病例

可以做的短得多,但这很有趣。


3

PowerShell v2 +,81字节

param($n)($b=$n[$n.count..0]-join','-replace'(\d+),\1','($1*2)'|iex)[$b.count..0]

将输入作为一个显式数组$n,将其反转$n[$n.count..0],将-joins个元素与一个逗号一起,然后将正则表达式-replace与第一个元素a匹配的数字对与正则表达式*2包围。产生的管道(用于输入的管道@(2,2,4,4)看起来像(4*2),(2*2))到iex(的缩写Invoke-Expression,类似于eval),将乘法转换为实际数字。将结果得到的数组$b,封装在括号将它放置在管道上,然后反转$b[$b.count..0]。将结果元素留在管道上,并且输出是隐式的。


测试用例

注意:在PowerShell中,“返回”一个空数组的概念是没有意义的-它$null会在离开范围后立即转换为空数组-因此,它等同于不返回任何内容,这是第一个示例中的操作(经过一些邪恶的冗长的错误)。此外,此处的输出以空格分隔,因为这是字符串化数组的默认分隔符。

PS C:\Tools\Scripts\golfing> @(),@(2,2,4,4),@(2,2,2,4,4,8),@(2,2,2,2),@(4,4,2,8,8,2),@(1024,1024,512,512,256,256),@(3,3,3,1,1,7,5,5,5,5)|%{"$_ --> "+(.\2048-like-array-shift.ps1 $_)}
Invoke-Expression : Cannot bind argument to parameter 'Command' because it is an empty string.
At C:\Tools\Scripts\golfing\2048-like-array-shift.ps1:7 char:67
+   param($n)($b=$n[$n.count..0]-join','-replace'(\d+),\1','($1*2)'|iex)[$b.count. ...
+                                                                   ~~~
    + CategoryInfo          : InvalidData: (:String) [Invoke-Expression], ParameterBindingValidationException
    + FullyQualifiedErrorId : ParameterArgumentValidationErrorEmptyStringNotAllowed,Microsoft.PowerShell.Commands.InvokeExpressionCommand

Cannot index into a null array.
At C:\Tools\Scripts\golfing\2048-like-array-shift.ps1:7 char:13
+   param($n)($b=$n[$n.count..0]-join','-replace'(\d+),\1','($1*2)'|iex)[$b.count. ...
+             ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : InvalidOperation: (:) [], RuntimeException
    + FullyQualifiedErrorId : NullArray

 --> 
2 2 4 4 --> 4 8
2 2 2 4 4 8 --> 2 4 8 8
2 2 2 2 --> 4 4
4 4 2 8 8 2 --> 8 2 16 2
1024 1024 512 512 256 256 --> 2048 1024 512
3 3 3 1 1 7 5 5 5 5 --> 3 6 2 7 10 10


3

脑爆裂,60字节

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

在线尝试!

说明:

{({}<>)<>}<>   Reverse stack

{   While input exists
  (
    ({}<>)   Push copy of last element to the other stack
    <>[({})] And subtract a copy of the next element
  )   Push the difference
  {   If the difference is not 0
    ((<{}>)) Push two zeroes
  }{}  Pop a zero
  {   If the next element is not zero, i.e the identical element
    ({}<>{})  Add the element to the copy of the previous element
    (<>)      Push a zero
  }{}    Pop the zero
}<>  End loop and switch to output stack

2

Python 2,94字节

def f(a,r=[]):
 while a:
    if len(a)>1and a[-1]==a[-2]:a.pop();a[-1]*=2
    r=[a.pop()]+r
 print r

在线尝试


2

朱莉娅73 82字节

f(l)=l==[]?[]:foldr((x,y)->y[]==x?vcat(2x,y[2:end]):vcat(x,y),[l[end]],l[1:end-1])

使用向右折叠可从后到前构建列表(也可以使用向左折叠并在开头和结尾处反转列表)。

如果当前列表的开头不等于下一个要添加的元素,则只需将其添加在前面。

否则,删除列表的开头(听起来有点残酷),并在元素前面加上2。

f([3,3,3,1,1,7,5,5,5,5]) 
returns a new list:
[3,6,2,7,10,10]

2

球拍166字节

(λ(l)(let g((l(reverse l))(o '()))(cond[(null? l)o][(=(length l)1)(cons(car l)o)]
[(=(car l)(second l))(g(drop l 2)(cons(* 2(car l))o))][(g(cdr l)(cons(car l)o))])))

取消高尔夫:

(define f
  (λ (lst)
    (let loop ((lst (reverse lst)) 
               (nl '()))
      (cond                            ; conditions: 
        [(null? lst)                   ; original list empty, return new list;
               nl]
        [(= (length lst) 1)            ; single item left, add it to new list
              (cons (first lst) nl)]
        [(= (first lst) (second lst))  ; first & second items equal, add double to new list
              (loop (drop lst 2) 
                    (cons (* 2 (first lst)) nl))]
        [else                          ; else just move first item to new list
              (loop (drop lst 1) 
                    (cons (first lst) nl))]  
        ))))

测试:

(f '[])
(f '[2 2 4 4]) 
(f '[2 2 2 4 4 8]) 
(f '[2 2 2 2]) 
(f '[4 4 2 8 8 2])
(f '[1024 1024 512 512 256 256]) 
(f '[3 3 3 1 1 7 5 5 5 5])
(f '[3 3 3 1 1 7 5 5 5 5 5])

输出:

'()
'(4 8)
'(2 4 8 8)
'(4 4)
'(8 2 16 2)
'(2048 1024 512)
'(3 6 2 7 10 10)
'(3 6 2 7 5 10 10)

1

Japt,12个字节

ò¦ ®ò2n)mxÃc

在线尝试!

开箱及其工作方式

Uò!= mZ{Zò2n)mx} c

Uò!=    Partition the input array where two adjacent values are different
        i.e. Split into arrays of equal values
mZ{     Map the following function...
Zò2n)     Split into arrays of length 2, counting from the end
          e.g. [2,2,2,2,2] => [[2], [2,2], [2,2]]
mx        Map `Array.sum` over it
}
c       Flatten the result

Jonathan Allan的Jelly解决方案中得到了一些想法。


0

Mathematica,51个字节

Abs[#//.{Longest@a___,x_/;x>0,x_,b___}:>{a,-2x,b}]&

{Longest@a___,x_/;x>0,x_,b___}匹配包含两个连续的相同正数的列表,并将这两个数转换为-2xLongest强制比赛尽可能晚进行。

该过程将逐步说明:

   {3, 3, 3, 1, 1, 7, 5, 5, 5, 5}
-> {3, 3, 3, 1, 1, 7, 5, 5, -10}
-> {3, 3, 3, 1, 1, 7, -10, -10}
-> {3, 3, 3, -2, 7, -10, -10}
-> {3, -6, -2, 7, -10, -10}
-> {3, 6, 2, 7, 10, 10}

0

Vim,28个字节

G@='?\v(\d+)\n\1<C-@>DJ@"<C-A>-@=<C-@>'<CR>

正则表达式的宏向后搜索匹配的连续数字,并将它们加在一起。

输入数组必须为每行一个数字。这种格式可以节省笔画,这很好,但是真正的原因是要解决重叠的正则表达式匹配项。给定字符串222,如果您/22仅匹配第一对,则不匹配重叠的第二对。当两对在不同的行开始时,重叠规则是不同的。在该挑战[2, 2, 2]变得[2, 4],所以匹配的重叠对是至关重要的。

注意:挑战仅要求一次通过。因此,您需要拥有:set nowrapscan。随着:set wrapscan我能有这样的结束对多个通道的工作,但因为写这种解决方案并不总是做一个版本。

  • <C-@>:通常,在命令行中,要在<CR>不运行命令的情况下键入文字,则必须使用对其进行转义<C-V>。但是您可以键入不<C-@>转义的字符,它将被视为<C-J>/ <NL>,就像<CR>运行宏时一样,而不是在键入时。尝试阅读:help NL-used-for-Nul
  • @=:这次我不能轻易使用录制的宏,因为输入可能没有匹配的对。如果在运行宏时发生这种情况,则不成功的搜索将使宏失败。但是,如果它在(隐式优先)记录过程中发生,则其余的正常模式命令将运行,从而损坏文件。缺点@=是我在递归调用中丢失了一个字节;有时,您可以将其@@用作递归调用,但是@"在这种情况下,该调用要从4个字节开始。
  • DJ@"<C-A>-DJ删除行并将数字(不包含换行符)放入寄存器,因此我可以将其作为用作数字参数的宏<C-A>。我必须-事后才能避免在类似的情况下再次比赛[4, 2, 2]

0

Perl6,92个字节

{my @b;loop ($_=@^a-1;$_>=0;--$_) {@b.unshift($_&&@a[$_]==@a[$_-1]??2*@a[$_--]!!@a[$_])};@b}

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.