Bring Out The Inner Llama Of A Sentence
Answer :
Perl, 52 bytes
The solution is provided as function that takes the string as argument and returns a list of positions.
One-based positions, case-sensitive search, without newlines: 52 bytes
sub l{pop=~/(l).*?(l).*?(a).*?(m).*?(a)/;@+[1..$#+]}
The case-sensitive search returns an empty array in the example of the question, because after matching the first three letters the lowercase letter
m
is missing in the input text.Support of newlines: + 1 byte = 53 bytes
sub l{pop=~/(l).*?(l).*?(a).*?(m).*?(a)/s;@+[1..$#+]}
The text can now span several lines.
Case-insensitive search: + 1 byte = 54 bytes
sub l{pop=~/(l).*?(l).*?(a).*?(m).*?(a)/si;@+[1..$#+]}
Now the example in the question reports a list of index positions, they are one-based numbers:
[45 68 77 106 115]
Zero-based positions: + 9 bytes = 63 bytes
sub l{pop=~/(l).*?(l).*?(a).*?(m).*?(a)/si;map{$_-1}@+[1..$#+]}
Result for the example in the question:
[44 67 76 105 114]
Ungolfed:
The latter variant includes more or less the other variants.
sub l { # pop() gets the last argument pop() =~ /(l).*?(l).*?(a).*?(m).*?(a)/si; # the letters inbetween are matched against ".*?", # the non-greedy variant of ".*". Thus ".*?" # matches only as few as possible characters. # The modifier /i controls the case-sensitivity # and means ignore case. Without the case matters. # Modifier /s treats the string as single line, # even if it contains newlines. map { $_-1 } # subtract 1 for zero-based positions @+[1..$#+] # Array @+ contains the end-positions of the last # submatches, and of the whole match in the first position. # Therefore the first value is sliced away. # @+ is available since Perl 5.6. } # test my @result = l(<<"END_STR"); Pie is good. I just ate a bunch of pies early this morning. Actually, it was closer to the afternoon. Mornings are good. END_STR print "[@result]\n";
sed, 299+1
Yes, sed can find a llama. No, sed can't do math. This is the longest answer so far, at 299+1 characters, because I had to teach sed to count.
This answer requires a sed with extended regular expressions (sed -E
or sed -r
). I used OpenBSD sed(1). Input is one string per line. (Therefore, the string may not contain a newline.) Output is a line of numbers, or nothing.
Usage (+1 character for -r
):
$ echo 'All arms on all shoulders may ache.' | sed -rf llama.sed 1 2 12 26 30
Source code (299 characters):
s/%/z/g s/(.*)[Aa]/\1%/ s/(.*)[Mm](.*%)/\1%\2/ s/(.*)[Aa]((.*%){2})/\1%\2/ s/(.*)[Ll]((.*%){3})/\1%\2/ s/(.*)[Ll]((.*%){4})/\1%\2/ /(.*%){5}/!d s/[^%]/z/g :w s/(z*)%/\10 z\1/ s/z*$// s/z0/1/ s/z1/2/ s/z2/3/ s/z3/4/ s/z4/5/ s/z5/6/ s/z6/7/ s/z7/8/ s/z8/9/ s/([0-9]z*)z9/z\10/g s/(z*)z9/1\10/ /[%z]/bw
The program first replaces the llama with five %
. (All %
in this program are literal.) The first command s/%/z/g
changes any %
to z
in the input line. The next five commands find the llama, so All arms on all shoulders may ache. becomes A%% arms on %ll shoulders %ay %che. Because each .*
is greedy, I always finds the llama on the right: llama llama would become llama %%%%%. If I can't get five %
, then /(.*%){5}/!d
deletes the input line and skips the next commands.
s/[^%]/z/g
changes every character but %
to z
. Then I enter a loop. s/(z*)%/\10 z\1/
changes the first %
to 0
, copies zero or more z
from left to right, and adds one more z
to right. This is so the number of z
will equal the index. For example, zz%zzz%...
becomes zz0 zzzzzzzz%...
because the first %
was at index 2, and the next %
is at index 8. s/z*$//
removes extra z
from the end of the string.
The next eleven commands count z
by removing each z
and counting up from 0
. It counts like zzz0
, zz1
, z2
, 3
. Also, 1zzzz9
becomes z1zzz0
(later 23
), or zzzz9
becomes 1zzz0
(later 13
). This loop continues until there are no more %
or z
.
CJam - 33
lel"llama"{1$#)_T+:T\@>}/;]___|=*
It gets the 1-based indexes (2 more bytes for 0-based)
Explanation:
l
reads a line from the input (replace with q
for whole input)
el
converts to lowercase
"llama"{...}/
executes the block for each "llama" letter
1$
copies the current string
#
finds the index of the letter
)_
increments and duplicates
T+:T
adds T (initially 0), updates T and leaves it on the stack
\@
swaps items around, now we have current-T, index, string
>
slices the string starting at the index
;
pops the remaining string
]
gathers the indexes in an array
At this point we have all the 1-based indexes; iff any letter was not found, the array will have duplicates.
___
makes 3 more copies of the array
|
(with 2 array copies) removes duplicates
=
compares, resulting in 0 if there were duplicates or 1 if not
*
multiplies the array 0 or 1 times accordingly
Comments
Post a Comment