25
\$\begingroup\$

Taken straight from the ACM Winter Programming Contest 2013. You are a person who likes to take things literally. Therefore, for you, the end of The World is ed; the last letters of "The" and "World" concatenated.

Make a program which takes a sentence, and output the last letter of each word in that sentence in as little space as possible (fewest bytes). Words are separated with anything but letters from the alphabet (65 - 90, 97 - 122 on the ASCII table.) That means underscores, tildes, graves, curly braces, etc. are separators. There can be more than one seperator between each word.

asdf jkl;__zxcv~< vbnm,.qwer| |uiop -> flvmrp
pigs, eat dogs; eat Bob: eat pigs -> ststbts
looc si siht ,gnitirw esreveR -> citwR
99_bottles_of_beer_on_the_wall -> sfrnel

\$\endgroup\$
4
  • \$\begingroup\$ Could you add a test case including digits and underscores? \$\endgroup\$
    – grc
    Mar 12 '13 at 4:56
  • 16
    \$\begingroup\$ The world ends in ed? I knew vim and Emacs couldn't measure up! \$\endgroup\$
    – Joe Z.
    Mar 13 '13 at 3:29
  • 1
    \$\begingroup\$ Well, the “real men use ed” essay has been part of the Emacs distribution for as long as I can remember. \$\endgroup\$
    – J B
    Apr 11 '13 at 15:07
  • \$\begingroup\$ Will the inputs be ASCII only? \$\endgroup\$
    – Phil H
    Mar 26 '18 at 14:21

50 Answers 50

1
2
0
\$\begingroup\$

Smalltalk, Squeak/Pharo flavour
122 char with traditional formatting for this method added to String:

endOfWords
    ^(self subStrings: (CharacterSet allCharacters select: #isLetter) complement) collect: #last as: String

62 chars in Pharo 1.4, with regex and weird formatting

endOfWords^''join:(self regex:'[a-zA-Z]+'matchesCollect:#last)
\$\endgroup\$
0
\$\begingroup\$

J: 60 characters (or 38 characters for a less correct version)

(#~e.&(,26&{.&(}.&a.)"0(97 65))){:&>;:]`(' '"_)@.(e.&'_:')"0

If we're willing let the program break whenever there are words ending in a colon or an underscore, then we can simplify this to 38 characters.

(#~e.&(,26&{.&(}.&a.)"0(97 65))){:&>;:

Sample run:

    (#~e.&(,26&{.&(}.&a.)"0(97 65))){:&>;:]`(' '"_)@.(e.&'_:')"0'asdf jkl;__zxcv~< vbnm,.qwer| |uiop'
flvmrp
    (#~e.&(,26&{.&(}.&a.)"0(97 65))){:&>;:]`(' '"_)@.(e.&'_:')"0'pigs, eat dogs; eat Bob: eat pigs'
ststbts
    (#~e.&(,26&{.&(}.&a.)"0(97 65))){:&>;:]`(' '"_)@.(e.&'_:')"0'99_bottles_of_beer_on_the_wall'
sfrnel
\$\endgroup\$
4
  • 1
    \$\begingroup\$ 38 Bytes (for a correct version): (#~[:2&|64 90 96 122&I.@(u:inv)){:&>;:, or 43 bytes for a non-explicit version: (#~[:2&|64 90 96 122&I.@(u:inv))@:({:@>)@;:. This uses the interval index verb, I., which interprets 64 90 96 122 as the set of intervals (__, 64] (64, 90], (90, 96], (96, 122], (122, _), and returns the index of the iterval to which its argument, the ascii code of the char, belongs. If this index is odd, it's not alphabetical. \$\endgroup\$ Mar 14 '18 at 23:32
  • \$\begingroup\$ @BolceBussiere doesn’t work with underscores for some reason (last test case). \$\endgroup\$
    – FrownyFrog
    Mar 16 '18 at 14:43
  • \$\begingroup\$ @FrownyFrog ah, I see why, ;: interprets abc_ as one word since variable names can contain underscores. +10 bytes to add (#~~:&'_'), probably an inefficient fix \$\endgroup\$ Mar 17 '18 at 2:13
  • \$\begingroup\$ @BolceBussiere that’s just '_'-.~ or something similar. \$\endgroup\$
    – FrownyFrog
    Mar 17 '18 at 11:52
0
\$\begingroup\$

It's in PHP. 197 bytes :( I'm beginner

$l=$_GET['line'];
$l=preg_replace('/(\W|_)+/',' ',$l);
$s=explode(' ',$l);
foreach($s as $d){
$a=substr($d,-1,1);
$o=ORD($a);
if(($o>=97 && $o<=122) || ($o>=65 && $o<=90)){
echo $a;
  }
}

EDITED Now it's 171 bytes

<?$l=$_GET['l'];$l=preg_replace('/(\W|_)+/',' ',$l);$s=explode(' ',$l);foreach($s as $d){$a=substr($d,-1,1);$o=ORD($a);if(($o>=97&&$o<=122)||($o>=65&&$o<=90)){echo$a;}}
\$\endgroup\$
5
  • 1
    \$\begingroup\$ For golf, you should as much as possible reduce your variable names to single characters at the very least. \$\endgroup\$
    – Gaffi
    May 24 '13 at 15:40
  • 1
    \$\begingroup\$ edited it.Thanks for telling me.I'm new here. \$\endgroup\$
    – Sasori
    May 24 '13 at 15:46
  • \$\begingroup\$ Sure. You may also want to look here for some additional PHP-specific help. \$\endgroup\$
    – Gaffi
    May 24 '13 at 18:30
  • \$\begingroup\$ foreach((' ',preg_replace('/(\W|_)+/',' ',$_GET['line'])) as $d){$a=substr($d,-1,1);$o=ORD();if(($o>=97 && $o<=122) || ($o>=65 && $o<=90)){echo $a;}} is 149, if it works. \$\endgroup\$ Feb 2 '18 at 17:19
  • \$\begingroup\$ \W|_ excludes digits; so you should add \d to your regex or use /[^a-z]+/i \$\endgroup\$
    – Titus
    Mar 16 '18 at 13:39
0
\$\begingroup\$

K 30

q)k)f:{x@&-1=-':(1_x," ")in,/.Q`a`A}
q)f "asdf jkl;__zxcv~< vbnm,.qwer| |uiop"
"flvmrp"
q)f "pigs, eat dogs; eat Bob: eat pigs"
"ststbts"
q)f "looc si siht ,gnitirw esreveR"
"citwR"
q)f "99_bottles_of_beer_on_the_wall"
"sfrnel"
\$\endgroup\$
0
\$\begingroup\$

SNOBOL4 (CSNOBOL4), 117 bytes

	L =&UCASE &LCASE
	N =INPUT
B	N SPAN(L) . X BREAK(L) REM . N	:F(O)
	X RPOS(1) LEN(1) . X
	O =O X	:(B)
O	OUTPUT =O
END

Try it online!

	L =&UCASE &LCASE			;*upper and lowercase letters
	N =INPUT				;*read input
B	N SPAN(L) . X BREAK(L) REM . N	:F(O)	;*while match success, set X to first run of L characters, split on non-L characters, and set N to remainder
	X RPOS(1) LEN(1) . X			;*set X to the last character of X
	O =O X	:(B)				;*append X to O, goto B
O	OUTPUT =O				;*output O
END

I feel like there's a way to directly set X to the end of the SPAN(L) but I haven't figured it out yet.

\$\endgroup\$
0
\$\begingroup\$

Haskell, 76 bytes

import Data.Char
u=isAlpha
f(a:i@(b:_))|u b=f i|u a=a:f i|1>0=f i
f(a:b)=[a]

Try it online!

\$\endgroup\$
0
\$\begingroup\$

Pyth, 27 bytes

Vc:z"[^A-Za-z]+"\ \ =+keN)k

Test suite

Pyth (indented) | Python 3 (translation)
                     | import re
                     | k=""
                     | z=input()
Vc:z"[^A-Za-z]+"\ \  | for N in re.sub("[^A-Za-z]+"," ",z).split(" "):
    =+keN)           |     k+=N[-1]
k                    | print(k)
\$\endgroup\$
0
\$\begingroup\$

PHP, 52 bytes

The narrow word characeter definition quite of narrows the number of approaches.
I wonder if anything else can compete with regex here.

Try them online.


<?=preg_filter("/.*?([a-z])[^a-z]+/i","$1",$argn._);

This requires PHP 7.1 for the negative string index. Run as pipe with -nF.


Another solution, 53 bytes (requires PHP 7.1)

foreach(preg_split("/[^a-z]/i",$argn)as$s)echo$s[-1];

Run as pipe with -nR.


The shortest non-regex solution I could find so far takes 60 bytes:

while(~$c=$argn[$i++])$b=ctype_alpha($c)?$c:!print$b;echo$b;

Run as pipe with -nR.

\$\endgroup\$
0
\$\begingroup\$

05AB1E, 17 bytes

Sžosåγ€g£ʒDáQ}€θJ

Try it online!

\$\endgroup\$
0
\$\begingroup\$

Gema, 12 characters

<L1>\X=$0
?=

Sample run:

bash-4.4$ gema '<L1>\X=$0;?=' <<< 'asdf jkl;__zxcv~< vbnm,.qwer| |uiop'
flvmrp
\$\endgroup\$
0
\$\begingroup\$

Add++, 81 bytes

D,u,@@,o
D,l,@,V91 65r123 97r+bU€CJGe
D,T,@~,
L~,€lB]ABcB*"."dV€uJG$tbU€TBZVcGbUJ

Try it online!

\$\endgroup\$
0
\$\begingroup\$

Julia 0.6, 46 bytes

f(s)=string(matchall(r"[a-z](?![a-z])"i,s)...)

Try it online!

Simple Julia with Standard regex. The r"" constructor creates a Regex class. Notice how the i letter after the last " makes the expression case insensitive.

\$\endgroup\$
0
\$\begingroup\$

Japt v2.0a0 -P, 16 9 6 bytes

q\L mÌ

Try it or run all test cases

\$\endgroup\$
1
  • \$\begingroup\$ I think you can just do f"%l(?!%l)" q (doesn't work in v2 because the parser doesn't like the (?) \$\endgroup\$ Feb 1 '18 at 20:05
0
\$\begingroup\$

Jelly, 12 bytes

e€ØẠ¬œpµLƇṪ€

Try it online!

e€ØẠ¬œpµLƇṪ€ - Main link. Takes a string S on the left
  ØẠ         - Yield "ABC...XYZabc...xyz"
 €           - For each character in S:
e            -   Is it in the alphabet?
    ¬        - Logical NOT
               This yields a list, L, with 1s at non-alphabet characters
     œp      - Partition S at truthy indices of L, without keeping the borders
       µ     - Begin a new chain with the partitioned list, P, as the argument
         Ƈ   - Keep those with a truthy:
        L    -   Length
           € - Over each:
          Ṫ  -   Yield the last character
\$\endgroup\$
0
\$\begingroup\$

sed (-E), 29 bytes

s/[a-zA-Z]*([a-zA-Z])|./\1/gp

Try it Online!

Beats other sed answers by 8 bytes. Port of the excellent ed solution.

\$\endgroup\$
0
\$\begingroup\$

JavaScript, 37 bytes

t=>t.match(/[a-z](?![a-z])/ig).join``
\$\endgroup\$
2
  • \$\begingroup\$ I don't think so. The question explicitly stated the letters should be output in as few bytes as possible (without separators) \$\endgroup\$
    – mbomb007
    Mar 14 '18 at 19:33
  • \$\begingroup\$ adding .join`` (7 bytes) will fix the output \$\endgroup\$ Nov 12 '20 at 3:45
0
\$\begingroup\$

JavaScript, 36 37 bytes

s=>s.match(/[A-z](?![a-z])/ig).join``
\$\endgroup\$
3
  • 1
    \$\begingroup\$ [A-z] matches characters outside the alphabet, so this is invalid. \$\endgroup\$
    – Razetime
    Jul 20 at 12:53
  • \$\begingroup\$ wut? what characters? @Razetime \$\endgroup\$
    – Luke_
    Jul 20 at 13:05
  • \$\begingroup\$ oh, []^_` didnt know nor expact that, thanks \$\endgroup\$
    – Luke_
    Jul 20 at 13:11
0
\$\begingroup\$

Python 3, 51 50 bytes

lambda s:re.sub("([a-zA-Z])*|.","\\1",s)
import re

Try it online!

How it works:

  • ([a-zA-Z])* will match a whole word capturing every single char in group 1 (one at a time).
  • if no words are found, . match the char to discard it
  • re.sub(<pattern>,"\\1",s) substitute every occurence of our pattern by the content of capturing group 1 (the last char of the word since it only captures one char at a time)
\$\endgroup\$
0
\$\begingroup\$

Pip, 8 bytes

DQ*a@+XA

Verify all test cases: Try it online!

Explanation

      XA  Built-in regex variable: `[A-Za-z]`
     +    Apply regex + quantifier: `[A-Za-z]+`
   a@     Find all matches in the input
DQ*       Dequeue the last character from each match
          Concatenate together and autoprint (implicit)
\$\endgroup\$
0
\$\begingroup\$

Haskell, 62 60 bytes

f s=last<$>words[last$' ':[c|'@'<c,c<'{','`'<c||c<'[']|c<-s]

Try it online! Thanks to Wheat Wizard for a version without the import, saving two bytes!


Haskell, 62 bytes

import Data.Char
f s=last<$>words[last$' ':[c|isAlpha c]|c<-s]

Try it online!

Given an input string s, [last$' ':[c|isAlpha c]|c<-s] converts all non-alphabetic characters to spaces. words splits the resulting string on white space and last<$> takes the last character of each of the resulting strings.

\$\endgroup\$
1
1
2

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.