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 - Nested

Flatten 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"
あ、この場合、string-upcaseで良かったですね…。
短いのは諦めるとしてちょっとひねった方法を思いついたので記念に投稿してみます。
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"

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

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

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

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

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

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"));
    }
}

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

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

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

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

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(/\b./){$&.upcase}'

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

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

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

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

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

GNU sed版

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

gorubyはKernel.#gsubがないのであまり短くならない。

1
goruby -pe'$_.gsub!(/\b./){$&.up}'

先ほどshebang版も投稿しましたが, 1.8.7ならこっちの方が短いですね.

1
ruby -pe'gsub /\b./,&:upcase'

#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

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

1
2
import Data.Char
main=interact$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
空白が2個以上続く場合におかしくなるのを
直していたら短くなった。
1
exit wd}:;<&(toupper&{.,}.);.2,&' '1!:1[3

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

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

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

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

scala llfuture.scala

みたいにします。

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

scalac -Xscript LLFuture llfuture.scala

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

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

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

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

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

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

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

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

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

> ;他の言語みたいに#/\b./でうまくいかないのは何故?

マニュアルにもあるのですが、 Gauche の regexp-replace-all は「文字列でマッチした部分の後ろの部分に ついて再帰的に自分自身を適用」するからです。 ^ の場合のときのことは覚えていたんですが \b でも問題になるんですねえ。

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

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()})
}

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

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

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

stdin → stdout

1
print"$System.in".replaceAll(/\b./){it.toUpperCase()}
空白を単語の区切りとしています。
文字列は標準入力で与えます。
1
File standardInput readLine split map(capitalize)join(" ")println
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);
}

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

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

ちまちま^^; 81byte

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

これぐらいが限界かも。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' "
GST で stdin → stdout。
1
P:=$ .stdin do:[:c|C:=c.'',{P}~'\w'or:[C:=c asUppercase].P:=C display]

Squeak Smalltalk で、組み込みのエディタ(a ParagraphEditor)に委譲してみる~の巻。

ゴルフに参加する(短くする)気ゼロで済みません汗。

1
2
3
4
5
| in editor |
in := 'this is a pen'.
editor := ParagraphEditor newParagraph: in asParagraph.
editor selectAll; makeCapitalized: nil.
^editor text asString   "=> 'This Is A Pen' "
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
<