LoginSignup
1
0

More than 3 years have passed since last update.

言語処理100本ノック 2020を解く(00. 文字列の逆順)

Last updated at Posted at 2020-05-19

はじめに

巷で流行っている自然言語100本ノックにPythonで挑戦してみます。
解き方はいろいろあると思うので、いろいろな解き方を示したいと思います。
全ての解き方を網羅することは当然ながらできません。
「こんな解き方があるよ」や「ここ間違ってるよ」という点があれば、ぜひコメントでお教えいただければと思います。

言語処理100本ノック 2020とは

言語処理100本ノック 2020は、東北大学の乾・鈴木研究室のプログラミング基礎研勉強会で使われている教材です。
詳しくは、言語処理100本ノックについてを参照して下さい。

Qiitaの記事へのリンク

1問目 2問目 3問目 4問目 5問目 6問目 7問目 8問目 9問目 10問目
第1章 00 01 02 03 04 05 06 07 08 09
第2章 10 11 12 13 14 15 16 17 18 19
第3章 20 21 22 23 24 25 26 27 28 29
第4章 30 31 32 33 34 35 36 37 38 39
第5章 40 41 42 43 44 45 46 47 48 49
第6章 50 51 52 53 54 55 56 57 58 59
第7章 60 61 62 63 64 65 66 67 68 69
第8章 70 71 72 73 74 75 76 77 78 79
第9章 80 81 82 83 84 85 86 87 88 89
第10章 90 91 92 93 94 95 96 97 98 99

00. 文字列の逆順

問題

00. 文字列の逆順の問題は、下記の内容です。

文字列”stressed”の文字を逆に(末尾から先頭に向かって)並べた文字列を得よ.

解答

問題を読むと、stressedを逆に並べた文字列を得る問題なので、答えはdessertsです。
以下に示すどの解答でも、最後の式を評価するとdessertsと返ってきます。

シンプル

素直に文字列の先頭に追加していく方法があります。
Pythonでは、+で文字列を結合して新たな文字列を生成できるのでした。

# シンプル
s = "stressed"

result = ""
for c in s:
    result = c + result
result #=> 'desserts'

関数型的

関数型的というにはシンプルすぎますが、関数の組み合わせで表現できます。
reversed関数は、イテレータを与えると逆順に取り出すイテレータを返すので、空文字で連結して所望の結果を得ます。

# 関数型的
s = "stressed"

"".join(reversed(s)) #=> 'desserts'

Pythonic

Python的であることを Pythonic といいますが、このコードが一番Pythonicでしょうか。
リストや文字列に対して、s[start:end:step]でアクセスする方法をスライスと呼びます。
step-1を指定すると逆順になります。-1ずつ進むんですね。

# Pythonic
s = "stressed"

s[::-1] #=> 'desserts'

その他の解答

組み合わせ

上記を組み合わせた、下記のような方法もあります。
文字列の末尾や先頭に何か足すとコピーが発生しますし、リストの末尾以外に要素を追加してもコピーが発生します。
それを避けるために、リストの末尾に要素を追加してから、"".join で文字列化しています。

# シンプルと関数型的の組み合わせ
s = "stressed"

l = []
for c in reversed(s):
    l.append(c)
"".join(l) #=> 'desserts'

インデックスで回す

素直にインデックスで回していく方法もあります。
for n in range(len(s)):はインデックスに対してforを回すときの構文の一つです。
len(s)は文字列sの長さを、range(n)0,...,n-1を走査するためのイテレータを返します。

# reversed相当の処理を自力で行う
s = "stressed"

l = []
for n in range(len(s)):
    l.append(s[len(s)-n-1])
"".join(l) #=> 'desserts'

インデックスの取り方は別の方法があります。
enumerate(s)は、n=len(s)として(0,s[0]),...,(n-1,s[n-1])を走査するためのイテレータを返します。
使わない変数という意味を込めて、_も使っています。込めてるだけです。

# reversed相当の処理を自力で行う
s = "stressed"

l = []
for n,_ in enumerate(s):
    l.append(s[len(s)-n-1])
"".join(l) #=> 'desserts'

リスト内包表記

リスト内包表記でも書けます。

s = "stressed"

l = [c for c in reversed(s)]
"".join(l) #=> 'desserts'

しかし、よくよく考えるとジェネレータ内包表記でもいいんじゃないかと思えてきます。

s = "stressed"

g = (c for c in reversed(s))
"".join(g) #=> 'desserts'

そうすると、下記の関数型的に戻りますね。

# 関数型的
s = "stressed"

"".join(reversed(s)) #=> 'desserts'

振り返り

好みは人それぞれだと思いますが、私が一番好きな回答は下記です。

# シンプルと関数型的の組み合わせ
s = "stressed"

l = []
for c in reversed(s):
    l.append(c)
"".join(l) #=> 'desserts'

for文で逆に回したい感が伝わるのと、素直にリストに要素を追加している点がわかりやすいです。
また、実際の問題を解く際にも、一つ一つの要素に何か処理をして別のリストに入れるという処理をよく書くので、その基本形になっています。
"".join(reversed(s))s[::-1]はこの問題を解くには最適なのですが、追加の処理を入れにくくなっています。

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0