challenge 文字列の均等分割

一行の文字列を指定した数の行にできるだけ文字数が均等になるように分割してください.
ただし,除算や剰余算を使わないで書いてみてください.

sample = "ゆめよりもはかなき世のなかをなげきわびつゝあかしくらすほどに四月十よひにもなりぬれば木のしたくらがりもてゆく"

divid 4 sample =>
 "ゆめよりもはかなき世のなかを"
 "なげきわびつゝあかしくらすほ"
 "どに四月十よひにもなりぬれ"
 "ば木のしたくらがりもてゆく"

divid 5 sample => 
 "ゆめよりもはかなき世の"
 "なかをなげきわびつゝあ"
 "かしくらすほどに四月十"
 "よひにもなりぬれば木の"
 "したくらがりもてゆく"

divid 6 sample => 
 "ゆめよりもはかなき"
 "世のなかをなげきわ"
 "びつゝあかしくらす"
 "ほどに四月十よひに"
 "もなりぬれば木のし"
 "たくらがりもてゆく"

この問題は、除算だけでははく算術演算とか、文字列の長さをstrlenの類いで測るとかをしなくても、多分書けるのではないかと思います。

Posted feedbacks - Python

昼休みの時間にさくっと書いてみる。
割り算とほぼ同等なことをやってるあたりがダメダメだなぁ。

以下出力結果

dev4
ゆめよりもはかなき世のなかを
なげきわびつゝあかしくらすほ
どに四月十よひにもなりぬれ
ば木のしたくらがりもてゆく
dev5
ゆめよりもはかなき世の
なかをなげきわびつゝあ
かしくらすほどに四月十
よひにもなりぬれば木の
したくらがりもてゆく
dev6
ゆめよりもはかなき
世のなかをなげきわ
びつゝあかしくらす
ほどに四月十よひに
もなりぬれば木のし
たくらがりもてゆく
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
#-*- coding: utf-8 -*-

def dev(s,num):
    tempCount = [0]*num
    outStr = [""]*num
    i = 0
    for j in xrange(len(s)):
        tempCount[i] += 1
        i = i+1
        if i == num:
            i=0

    for x in xrange(num):
        outStr[x] = s[:tempCount[x]]
        s = s[tempCount[x]:]
    return outStr

#配列をそのままプリントするとユニコードがうまく表示されないので
def printArray(array):
    for x in array:
        print x
 
sample = u"ゆめよりもはかなき世のなかをなげきわびつゝあかしくらすほどに四月十よひにもなりぬれば木のしたくらがりもてゆく"
print "dev4"
printArray(dev(sample,4))
print "dev5"
printArray(dev(sample,5))
print "dev6"
printArray(dev(sample,6))

正規表現を使いました。

countは0以上の無限リストを作成する命令で、0以上の各整数iについて「『i文字またはi+1文字』のブロックがn個ある」という正規表現を作ってマッチを試みます。マッチすればグループ分けをreturn。greedyなので前半のグループが長い方、後半のグループが短い方になります。

1
2
3
4
5
6
7
8
def divid(n, s):
    import re
    from itertools import count
    for i in count():
        pat = "^%s$" % "(.?.{%d})" % i * n
        m = re.match(pat, s)
        if m:
            return m.groups()

文字数数えるのが面倒だったので、スライスでn個飛ばしの文字列を作って、それの文字数を使いました。
オフセット数えるのも、面倒なので再帰任せです。
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
# encoding: utf-8

sample = u"ゆめよりもはかなき世のなかをなげきわびつゝあかしくらすほどに四月十よひにもなりぬれば木のしたくらがりもてゆく"

def divid(str, n, target_list=[]):
  def get_splitted_str(str, n):
    return str[:len(str[::n])]

  if str:
    s = get_splitted_str(str, n)
    return divid(str[len(s):], n-1, target_list+[s])
  else: return target_list

for i in range(4,7):
  print "divid", i
  l = divid(sample, i)
  for line in l:
    print line
  print ""

なるほど。その発想を使ってみました。 cycleは与えられたリストを巡回し続ける無限リストで、zipは短い方に会わせて切り詰めます。

これでも算術演算やstrlenは必要ないですね…正規表現を使うよりは黒魔術っぽさがなくていいかも。

>>> cycle(range(3))
<itertools.cycle object at 0x0270AAA8>
>>> zip(_, "hoge")
[(0, 'h'), (1, 'o'), (2, 'g'), (0, 'e')]
1
2
3
4
5
def divid(n, s):
    from itertools import cycle
    xs = zip(cycle(range(n)), s)
    return ["".join(c for i, c in xs if i == j)
             for j in range(n)]

算術演算も文字列長取得関数も使っていません。
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
#-*- coding: utf-8 -*-

def divid(n, s):
  s = unicode(s, 'utf-8')
  sl = list(s)
  l = []
  while s:
    l.append(list(s[:n]))
    s = s[n:]

  print '\ndivid %d sample =>' % n
  while l[0]:
    print ' "%s"' % ''.join([a.pop(0) and sl.pop(0) for a in l if a])

sample = 'ゆめよりもはかなき世のなかをなげきわびつゝあかしくらすほどに四月十よひにもなりぬれば木のしたくらがりもてゆく'

print '"sample = %s"' % sample
divid(4, sample)
divid(5, sample)
divid(6, sample)

Index

Feed

Other

Link

Pathtraq

loading...