String replicator

In Vim, you can repeat a command by preceding it with a number, like 3dd is equivalent to dd dd dd. Well, this repeating pattern is not restricted to Vim commands. String can be replicated in this way, too.

Specification:

Given a string, consisting of only digits, alphabetical characters (both upper-case and lower-case) and spaces, with an optional trailing newline, as input, write a program that does the following job:

• Each "word" consists of digits and alphabets. If a letter is preceded with a number (there may be more than one digit in a number, or the number is zero), repeat that letter for the given times. For example:

a2bc -> abbc
3xx1yz -> xxxxyz
10ab0c0d0e -> aaaaaaaaaab # No 'cde' because there's a zero
2A2a2A2a -> AAaaAAaa

• Words are separated by spaces. There's a maximum of one space between every two adjacent words.

Easy, right? Here's the additional stuff:

• If there's a number before the space, repeat the next word for the given times. The number will always be attached to the end of the previous word, or at the start of the string. Example:

a2bc3 2d -> abbc dd dd dd
3 3a -> aaa aaa aaa
33a -> aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
0 abcd0 efgh3 2x -> xx xx xx
a3 0xc b -> a c c c b

• If an empty word ought to be repeated, don't output multiple spaces in a row. Squash them:

a3 0x2 b -> a b b   # NOT 'a    b b'


In other words, your program should never output two spaces together.

• Input is never empty, but it's not necessary for the output to be non-empty:

0 3x -> (empty)

• Input and output can be taken in any preferred ways. A function taking input from arguments and giving output via return values is acceptable as well.

If it's a program, it must not exit with error (i.e. return value is zero).

• Numbers are always decimal, and never starts with a zero, unless the number itself is zero, in which case there's only one zero. I.e. you don't need to consider 077a or 000a given as input.

• All numbers are under 2^31 (2,147,483,648). Maximum output length is under 2^32 (4,294,967,296) bytes.

• The program may optionally output one trailing space and/or one trailing newline. Those space and newline do not affect the validity of output. Even if the correct output should be empty, an output of a space followed by a newline will qualify.

In short, a valid input matches this regular expression:

([0-9]+ )?([0-9A-Za-z]*[A-Za-z])([0-9]* [0-9A-Za-z]*[A-Za-z])*( ?\n?)


And for a valid output:

([A-Za-z]+)( [A-Za-z]+)*( ?\n?)


Sample test cases:

abcdefg -> abcdefg
a3bcd -> abbbcd
a3bbbc -> abbbbbc
3a0b -> aaa
abc 3d -> abc ddd
abc3 d -> abc d d d
5 1x5 1y0 z -> x x x x x y y y y y
a999 0x b -> a b
999 0s -> (empty)
0 999s -> (empty)
0 999s4 t -> t t t t
a3 0xc b -> a c c c b
ABC3 abc -> ABC abc abc abc


This is a , so the shortest program in bytes in each language wins!

• .... "program must not exit with error" "input must not be given as a list of character ..." any particular reason? (as you already knew) we generally allow flexible I/O format. – user202729 Jan 8 '18 at 6:04
• @user202729 I'm consider removing the latter. For the program exit result I want to keep it. Edit: Done. – iBug Jan 8 '18 at 6:05
• – cole Jan 8 '18 at 6:25
• Also similar – danieltakeshi Jan 8 '18 at 16:31
• I think a test like a3 0xc b -> a c c c b should be added, as I originally had code that worked for all the test cases above, but didn't work correctly for that. – Brad Gilbert b2gills Jan 8 '18 at 18:15

JavaScript (Node.js), 102129110 106 bytes

s=>s[p="replace"](/(\d+)( \w*[A-Z])/gi,g=(_,a,b)=>b.repeat(a))[p](/(\d+)(.)/g,g)[p](/ +/g,(_,i)=>i?" ":"")


Try it online!

Thanks for @Arnauld for -4 bytes.

Perl 6, 88 bytes

{$_=$^a;s:g/(\d+):(\w)/{$1 x$0||'_'}/;s:g/(\d+)\s([\w& \D]+)/ {$1 xx$0}/;~S:g/_//.words}


Test it

Expanded:

{ # bare block lambda with placeholder parameter ｢$a｣ # store a copy of the argument in ｢$_｣
# (shorter than ｢-> $_ is copy {…}｣)$_ = $^a; # note that ｢$_｣ is the default scalar,
# and many things operate on it by default (like ｢s///｣)

# do the character repeats
s :global
/

(\d+)           # repeat count
:               # don't backtrack (prevents it from matching word repeats)
(\w)            # character to repeat

/{

$1 x$0         # do the repeat

|| '_'          # replace with ｢_｣ if the repeat was 0 (matched by [\w & \D])
# this is so “words” don't get removed yet

}/;

# do the word repeats
s :global
/

(\d+)           # repeat count

\s              # shortest way to match a space

([
\w & \D       # word character and not a digit (doesn't match next repeat)
]+)             # match that at least once

/ {               # add a space (as we removed it by matching it)

$1 xx$0        # list repeat (adds a space between values when stringified)

}/;

# the following is the result
~                 # stringify (adds spaces between values in a list) # (3)
S :global /_//  # remove all _ not in-place                        # (1)
.words          # get a list of words                              # (2)
}


The ~(…).words combination removes extraneous spaces, which is useful if a “word” gets removed.

Python 2, 286275260257 238 bytes

-19 bytes thanks to ovs

def f(s,j=' '.join):exec"s=s.split(%s[-1]):s[i]=s[i][:-1];s[i-1]=j([s[i-1]]*int(w[-1]))\ns=list(j(s[::-1])%s):s[i]='';s[i-1]*=int(w)\nprint j(''.join(s[::-1]).strip().split())"%((')[::-1]\nfor i,w in enumerate(s):\n if str.isdigit(w',)*2)


f takes a string as an argument and prints the formatted string.

Here's a repl.it with the test cases.

Ungolfed code:

def f(s, j=' '.join):
s = s.split()[::-1]
for i, w in enumerate(s):
if str.isdigit(w[-1]):
s[i] = s[i][:-1]
s[i - 1] = j([s[i - 1]] * int(w[-1]))
s = list(j(s[::-1]))[::-1]
for i, w in enumerate(s):
if str.isdigit(w):
s[i] = ''
s[i - 1] *= int(w)
print j(''.join(s[::-1]).strip().split())


Still working on improvements.

• 238 bytes – ovs Jan 8 '18 at 15:24
• @ovs Thanks. Can't believe I didn't think of getting rid of the newline and indent for the exec, since it's the only line in the function. – nog642 Jan 8 '18 at 19:31

s/\d+( .*?)(\d*)( |$)/$1x$&.$2.$3/eg&&redo;s/\d+(\D)/$1x$&/eg;s/ +/ /g;s;^ +;  Try it online! Clean, 443 ... 306 bytes import StdEnv,StdLib ^ =last$n|n>"9"=1=toInt n
?v c| ^v<c=init v=v
q=groupBy
f[a:t]|a<"a"=repeatn($a)(hd t)++f(tl t)|t>[]=[a:f t]=[a," "] f e=e @l#[h:t]=[[toString[c:if(c<'1')[]k]\\[c:k]<-q(\a b=max a b<'a')s]\\s<-q(\a b=min a b>' ')l|s>[' ']] =flatten(map f[?h"a":[?u":"\\u<-t&v<-map^[h:t],_<-[1..$v]]])


Try it online!

Lua, 113 bytes

a="(%d+)(%a)"g=a.gsub function r(c,s)return s:rep(c)end g(g(g(g(...,a,r),"(%d+)( %a*)",r)," +"," "),"%a.*",print)


Try it online!