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

Popular posts from this blog

Converting A String To Int In Groovy

"Cannot Create Cache Directory /home//.composer/cache/repo/https---packagist.org/, Or Directory Is Not Writable. Proceeding Without Cache"

Android How Can I Convert A String To A Editable