challenge LL Golf Hole 2 - 文字列に含まれる単語の最初の文字を大文字にする

文字列に含まれる単語について、それぞれの単語の最初の文字を大文字にしてください。

たとえば、"LL future" と与えられたときは "LL Future" と出力する。"LL day and night" と与えられたときは "LL Day And Night" と出力する。

与えられる文字列はリテラルで表記する、標準入力で与えられる、引数で与えられるなどは自由とします。

余力のあるものはこのプログラムを短くしてみたり、短くしてみたり、短くしてください。

※LL Future実行委員の高野光弘です。この出題は LL Future公式の出題であり、優れたものについてはLL Golfのセッションでご紹介させていただくかもしれません。ご理解の上、ご投稿ください。また、LL Futureのチケットは現在も発売中です。よろしければ、メインイベントの方にもぜひご参加ください。

1
ruby -e "p 'LL day and night'.scan(/(\w)(\w*)/).map{|a|a.shift.upcase + a.to_s}.join(' ')"

Posted feedbacks - Flatten

Nested Hidden
1
print ' '.join([i.capitalize() for i in 'LL day and night'.split()])

全然短くできないですね(^^;
最初の1文字だけ大文字というのなら標準でいくつか方法があるのですが…。
1
2
(format()"~{~A~^ ~}"(mapcar(lambda(x)(string-capitalize x :end 1))(ppcre:split"\\s+""LL day and night")))
;=> "LL Day And Night"

短いのは諦めるとしてちょっとひねった方法を思いついたので記念に投稿してみます。
1
2
3
4
5
(defun cap (stream string &rest args)
  (princ (string-capitalize string :end 1) stream))

(format nil "~{~/cap/~^ ~}" (ppcre:split"\\s+" "LL day and night"))
;=> "LL Day And Night"

あ、この場合、string-upcaseで良かったですね…。

文字列は標準入力で与えます。

お題のコードは、"hello, i am a cat"みたいな文が"Hello, Am Cat"になってしまうような。

1
ruby -pe'$_.gsub!(/(\w)(\w*)/){$1.upcase+$2}'

1
2
3
4
before = "LL day and night".split(' ');
after = [];
while(i = before.shift()) after.push(i.charAt(0).toUpperCase() + i.slice(1));
alert(after.join(' '));

普通に正規表現で単語を切り出しています。単語の定義は、「Unicodeの文字カテゴリに属する文字の連続」としています(日本語の正書法の定義とは違います)。大文字は単純に "Upper Case" です("Title Case" ではありません)。

サンプルコードの実行結果は以下です。

LL Day And Night.
日本語abc漢字
Αβγ
A=B+C
Één
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import java.util.regex.*;

public class Upper {
    private final static Pattern WORD = Pattern.compile("(\\p{L})(\\p{L}*)");

    public static String toUpper1st(String s) {
        Matcher m = WORD.matcher(s);
        StringBuffer sb = new StringBuffer();
        while (m.find()) {
            String rep = m.group(1).toUpperCase() + m.group(2);
            m.appendReplacement(sb, rep);        
        }
        m.appendTail(sb);
        return sb.toString();
    }
    
    public static void main(String[] args) {
        System.out.println(toUpper1st("LL day and night."));
        System.out.println(toUpper1st("日本語abc漢字"));
        System.out.println(toUpper1st("αβγ"));
        System.out.println(toUpper1st("a=b+c"));
        System.out.println(toUpper1st("één"));
    }
}

うおっ。お恥ずかしい限り・・・ ちょっと直しておきます。orz

何はともあれ、短いコードありがとうございます!


Rは文字列操作が苦手です。

1
2
3
capword <- function(str){
  sapply(strsplit(str, " "), function(s)(paste(toupper(substring(s,1,1)), substring(s,2), sep="", collapse=" ")))
}

改造。

1
ruby -pe'gsub!(/\w+/){$&.capitalize}'

RUBY_VERSION #=> "1.8.6"

1
"LL day and night".split.map{|i| i[0].chr.upcase + i[1..-1] }.join(' ')

文字列は標準入力から

1
ruby -pe'gsub(/\w+/){$&.capitalize}'

文字列は標準入力から

1
ruby -pe'gsub(/\b./){$&.upcase}'

しまった。言語を指定し忘れました。Rubyでお願いします。

それだけじゃなんなのでこれのPerl版。文字列は標準入力から

1
perl -pe's/\b./uc$&/ge'

#6902 のやつは、"Ll Day And Night"になってしまう("LL" -> "Ll")ので、もう一回。

1
print ' '.join([i[0].upper() + i[1:] for i in 'LL day and night'.split()])

無難に書いた。

1
2
import Data.Char
main=getLine>>=print.unwords.map(\(a:b)->toUpper a:b).words

windows 用のコンソール版の場合です。
空白を単語の区切りとしています。
文字列は標準入力で与えます。

>echo "LL day and night" | jconsole ll2.ijs
"LL Day And Night"
1
exit wd;(,' '&;)/<&(toupper&{.,}.);._1' ',1!:1[3

単純に縮めるだけならば次のようにー。

1
2
import Data.Char
main=interact$unwords.map(\(a:b)->toUpper a:b).words

ScalaのStringは基本的にJavaのと同じだけど、いくつかの便利なメソッドが追加されて(いるようにユーザからは見えて)います。capitalize()はそのようなメソッドの内の一つで、文字列の最初の文字を大文字にします。

あと、文字列の配列を結合するときに、文字列の区切りを強制的に空白にしてしまうので、タブ区切りだった場合、それに関する情報を失っちゃうのはちょっとビミョーではあります。

1
print(args(0).split("\\s").map(_.capitalize).mkString(" "))

入力は標準入力から。単語区切りはスペース。
1
print(readLine split" "map{_ capitalize}mkString" ")

追記です。標準入力から入力を受け取るとコードが長くなると、入力はコマンドライン引数から受け取るようになってます。あと、元のプログラムは、スクリプトを直接実行するモードで実行するためのものなので、scalac(クラスファイルへコンパイルするコンパイラ)ではなくscalaコマンドを使って、

scala llfuture.scala

みたいにします。

scalacでコンパイルしたい場合、-Xscriptオプションを付けて、

scalac -Xscript LLFuture llfuture.scala

のようにするとうまく行きます。


文頭はすでに大文字になっていることを仮定しています。

;; 最初 #/\b\w/ としてハマった。

1
2
3
(use srfi-13)
(define (main args)
  (print (regexp-replace-all #/\W\w/ (cadr args) (compose string-upcase (cut <> 0)))))

さらに追記というかtypoです。

誤:標準入力から入力を受け取るとコードが長くなると、

正:標準入力から入力を受け取るとコードが長くなるので、


上の訂正は、ログインし忘れたので匿名になってますが、みずしまが書いたものです。ダメダメですね。


フレームワーク無しだと、ここらで限界?
1
"LL day and night".replace(/(\w)(\w*)/g,function(a,b,c){return b.toUpperCase()+c;})

GNU sedで確認しました。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
:a
/\<[a-z].*/{
s//\n&/
h
s/.*\n\(.\).*/\1/
y/abcdefghijklmnopqrstuvwxyz/ABCDEFGHIJKLMNOPQRSTUVWXYZ/
H
g
s/\n.\(.*\)\n\(.\)$/\2\1/
b a
}

リテラルを変換するだけなら{}の中だけで良かったのだけれど… Stringにメソッドを追加する形にしてみました。

このコードを実行するとStringにcapitalizeメソッドが追加され、

>>> "LL future".capitalize()
"LL Future"
>>> "LL day and night".capitalize()
"LL Day And Night"

あるいは、

>>> ["LL future", LL day and night].capitalize()
["LL Future", "LL Day And Night"]

のように動作します。タブとかも大丈夫のはず…

1
2
3
4
5
String.metaClass.capitalize = {
    return delegate.replaceAll(
            /(¥s)(¥w)/,
            {all, s, w -> s + w.toUpperCase()})
}

空白を単語の区切りとしています。
文字列は標準入力で与えます。
1
File standardInput readLine split map(capitalize)join(" ")println

行頭が大文字じゃなくても大丈夫な様にしました。

あとちょこっとだけ短くしてみました。メソッド名、ほんとはcapitalizeWordsとかにすべきなのかなぁ?

1
String.metaClass.capitalize = {delegate.replaceAll(/(^|¥s)(¥w)/, {all, s, w -> s + w.toUpperCase()})}

VCEE2008でコンパイルできました。
LL Future
LL Day And Night
Hello, I Am A Cat
1
2
3
4
5
6
7
f(char *s){*s=toupper(*s);while(*++s)if(*(s-1)==' ')*s=toupper(*s);}

main(){
    char *s1="LL future",*s2="LL day and night",*s3="hello, i am a cat";
    f(s1);f(s2);f(s3);
    printf("%s\n%s\n%s\n",s1,s2,s3);
}

これぐらいが限界かも。CLISP で動作確認しました。

ただし char< で大文字が小文字より小さいと判定されるかどうかは実装依存なので、言語仕様上は期待した動作をすることは保障されません。

1
(defun cap(s)(map'string(lambda(a b)(if(char< a b)a b))s(string-capitalize s)))

なるほど!
空白で区切られた次の文字は大文字になるんですね…。
自分は、string-capitalizeの仕様を勘違いしてました(^^;

Squeak Smalltalk で。

1
2
3
4
5
6
| in prev |
in := 'LL day and night'.
prev := $-.
^in collect: [:char | prev := prev isLetter ifTrue: [char] ifFalse: [char asUppercase]]

"=> 'LL Day And Night' "

うーん、これで勘弁してください・・・

1
gsub("\\b(.)", "\\U\\1", "LL day and night", perl=TRUE)

Infernoシェルです。
1行に詰め込みすぎている気がします。

短くするために、以下の特徴を使っています。
1. コマンド引数は空白区切りの配列になる
2. 配列をechoすると空白区切りになる
3. sliceの文字数は、上限を超えてもかまわない

このため、空白の数は保持しません。

% sh uc LL day and night
% sh uc hello, i am a cat
1
2
3
4
load string

apply {b=($b ${toupper ${slice 0 1 $1}}^${$slice 1 ${len $1} $1})} $*
echo $b

$sliceになってました。$は要らないです。
1
2
3
4
load string

apply {b=($b ${toupper ${slice 0 1 $1}}^${slice 1 ${len $1} $1})} $*
echo $b

PostScript 不真面目版。スペースが2個続くとエラーで止まります。

余談ですが、s/与力/余力/ ですね。

1
2
3
4
5
6
7
[(LL future)(LL day and night)]/l{( )search exch 0 2 copy get -33 and put{pop l}if}def{dup l =}forall
% 出力
LL Future
LL Day And Night

% バイナリーエンコードする場合はこちらをエンコードしたものの方が短い
[(LL future)(LL day and night)]{dup{( )search exch 0 2 copy get -33 and put not{exit}if pop}loop =}forall

PostScript でもう少し真面目に。上のも含めて標準出力に出力します。

1
2
3
4
5
6
7
8
9
[(LL future)(LL day and night)(echo $home)(hello,  i am a cat.)]/l{( )search exch dup(a)ge 1 index({)lt and{0 2 copy get -33 and put[}if pop{pop l}if}def{dup l =}forall
% 出力
LL Future
LL Day And Night
Echo $home
Hello,  I Am A Cat.

% バイナリーエンコードする場合はこちらの方が短い
[(LL future)(LL day and night)(echo $home)(hello,  i am a cat.)]{dup{( )search exch dup(a)ge 1 index({)lt and{0 2 copy get -33 and put[}if pop not{exit}if pop}loop =}forall

標準入力で与えた文字列を変換して標準出力に返します。

実行例:

% gosh capitalize.scm
LL day and night
LL Day And Night

Lisp は Golf に向かないのですが、頑張って1文字にしてみました(遠くから見てください)。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
           (letrec
             ((a(#0=
              lambda(
               c)(cond
                #1=((memv
                 c'(#\tab
                  #\return 
                   #\newline
                  #\space))#2=
                 (#3=write-char 
              c)b)(else  #2#a))))
            (b(#0#(c)(    |cond|#1#
          ((<= 97(#4=       char->ucs
        c)122)(#3#            (ucs->char
      (-(#4#c)32)              ))a)(else
    #2#a)))))(                   port-fold(
#0#(|c|p)(p|c|)                 )|b|read-char))

ご指摘、ありがとうございまっす。 修正させていただきました〜。


XSLTもGolfには向きませんね。
本当は改行も全部消したかったんですが、
表示がエライことになるので止めました。
入力は↓のコード自身です。
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
<x:transform version="2.0"
xmlns:x="http://www.w3.org/1999/XSL/Transform"
xmlns:s="http://www.w3.org/2001/XMLSchema"
xmlns:f="http://www.w3.org/2005/xpath-functions">
<x:variable name="ll2008" as="s:string">LL future</x:variable>
<x:variable name="ll2005" as="s:string">LL day and night</x:variable>
<x:output method="text"/>
<x:template match="/">
<x:apply-templates select="/x:transform/x:variable"/>
</x:template>
<x:template match="x:variable">
<x:variable name="tokens" as="s:string*">
<x:for-each select="f:tokenize(./text(),' ')">
<x:sequence select="f:concat(f:upper-case(f:substring(.,1,1)),f:substring(.,2,f:string-length(.)-1))"/>
</x:for-each>
</x:variable>
<x:value-of select="f:concat(@name,'=')"/>
<x:value-of select="string-join($tokens,' ')"/>
<x:text>&#xA;</x:text>
</x:template>
</x:transform>

1.8↑
1
"lL day and night".replace(/\b./g,function($)$.toUpperCase())

stdin → stdout

1
print"$System.in".replaceAll(/\b./){it.toUpperCase()}

インタープリタ(erl)のコマンドラインで実行する形式です。
文字列は、string:tokens関数の第1引数として、
ダブルクォートでくくって指定しています。
単語の区切りを1文字の空白と仮定しています。
エスケープされた文字などについては考慮していません。
特にgolfを意識したことは自分にはできませんでした。
1
string:join(lists:map(fun([H|T])->[string:to_upper(H)]++T end,string:tokens("LL day and night"," "))," ").

stdin → stdout
;他の言語みたいに#/\b./でうまくいかないのは何故?
1
(use srfi-13)(print(regexp-replace-all #/(\w)(\w*)/(read-line)(rec,quote #`",(string-upcase'1),'2")))

GST で stdin → stdout。
1
P:=$ .stdin do:[:c|C:=c.'',{P}~'\w'or:[C:=c asUppercase].P:=C display]

参考ページに影響を受けてみた。入出力込みで84byte

1
a;main(){char*s=gets(&a);for(;*s;s++)if(&a==s||*(s-1)==' ')*s=toupper(*s);puts(&a);}

stdin → stdout
1
r=io.read():gsub('(%w)(%w+)',function(h,t)return h:upper()..t end)print(r)

ちまちま^^; 81byte

1
a;main(){char*s=gets(&a);for(;*s;s++)putchar(&a==s||*(s-1)==' '?toupper(*s):*s);}

そういえばUを使えばeは必要ないですね

1
perl -pe's/\b./\U$&/g'

GNU sed版

1
sed 's/\b./\U&/g'

Lazy Kで。666byte

$ echo "LL day and night" |lazy hole2.lazy
LL Day And Night
1
2
3
4
5
6
7
8
9