ひるあんどんブログ

色々なことに手を出してみるブログ


In computer science, a stack is a particular kind of data type or collection in which the principal operations in the collection are the addition of an entity to the collection (also known as push) and the removal of an entity (also known as pop). The relation between the push and pop operations is such that the stack is a Last-In-First-Out (LIFO) data structure. In a LIFO data structure, the last element added to the structure must be the first one to be removed. Often a peek, or top operation is also implemented, returning the value of the top element without removing it.

We will emulate the stack process with Python. You are given a sequence of commands:

  • "PUSH X" -- add X in the stack, where X is a digit.
  • "POP" -- look and remove the top position. If the stack is empty, then it returns 0 (zero) and does nothing.
  • "PEEK" -- look at the top position. If the stack is empty, then it returns 0 (zero).

The stack can only contain digits.

You should process all commands and sum all digits which were taken from the stack ("PEEK" or "POP"). Initial value of the sum is 0 (zero).

Let's look at an example, here’s the sequence of commands:
["PUSH 3", "POP", "POP", "PUSH 4", "PEEK", "PUSH 9", "PUSH 0", "PEEK", "POP", "PUSH 1", "PEEK"]

Command Stack Sum
PUSH 3 3 0
POP 0+3
POP 3+0
PUSH 4 4 3
PEEK 4 3+4
PUSH 9 4,9 7
PUSH 0 4,9,0 7
PEEK 4,9,0 7+0
POP 4,9 7+0
PUSH 1 4,9,1 7
PEEK 4,9,1 7+1=8

Input: A sequence of commands as a list of strings.

Output: The sum of the taken digits as an integer.

Example:

digit_stack(["PUSH 3", "POP", "POP", "PUSH 4", "PEEK", "PUSH 9", "PUSH 0", "PEEK", "POP", "PUSH 1", "PEEK"]) == 8

digit_stack(["POP", "POP"]) == 0

digit_stack(["PUSH 9", "PUSH 9", "POP"]) == 9

digit_stack([]) == 0


How it is used: Stacks have numerous applications. We see stacks in everyday life, from the books in our library, to the blank sheets of paper in our printer tray. All of them follow the Last In First Out (LIFO) logic, that is when we add a book to a pile of books, we add it to the top of the pile, whereas when we remove a book from the pile, we generally remove it from the top of the pile.

Precondition:
0 ≤ len(commands) ≤ 20;
all(re.match("\APUSH \d\Z", c) or с == "POP" or c == "PEEK" for c in commands)

def digit_stack(commands):
    stack = []
    sum_list = []
    
    def push(number):
        stack.append(int(number))
            
    def pop():
        if stack:
            sum_list.append(stack.pop())
            
    def peek():
        if stack:
            sum_list.append(stack[-1])
    
    for command in commands:
        command = command.split()
        
        if command[0] == "PUSH":
            push(command[1])
        elif command[0] == "POP":
            pop()
        elif command[0] == "PEEK":
            peek()
        
    return sum(sum_list)
        
            
    

if __name__ == '__main__':
    #These "asserts" using only for self-checking and not necessary for auto-testing
    assert digit_stack(["PUSH 3", "POP", "POP", "PUSH 4", "PEEK",
                        "PUSH 9", "PUSH 0", "PEEK", "POP", "PUSH 1", "PEEK"]) == 8, "Example"
    assert digit_stack(["POP", "POP"]) == 0, "pop, pop, zero"
    assert digit_stack(["PUSH 9", "PUSH 9", "POP"]) == 9, "Push the button"
    assert digit_stack([]) == 0, "Nothing"


これは、そんなに難しくなかったので素直にメソッドを小分けにしてコードがぐちゃぐちゃにならないように気をつけながら解きました。


1位のコード


def digit_stack(commands):

    stack, digitsum = [], 0

    for command in commands:

        if command.startswith("PUSH"):

            stack.append(int(command[-1]))

        elif stack:

            digitsum += stack[-1]

            if command == "POP":

                stack.pop()

    return digitsum


str.startswith(prefix[, start[, end]])¶(原文)

    文字列が指定された prefix で始まるなら True を、そうでなければ False を返します。 prefix は見つけたい複数の接頭語のタプルでも構いません。オプションの start があれば、その位置から判定を始めます。オプションの end があれば、その位置で比較を止めます。

ちなみに文字列の最後を調べる関数でendswithというのもありました。

sumをリストにためておいて最後に計算するのではなく、その都度計算しているけどこっちのほうが計算効率いいのかな。わからん。

Weekend counter


ソフィアはあなたに予定と二つの日付を与え、週末の計画を建てるのを助けて欲しいとあなたに言いました。 彼女はあなたに最初の日付から最後の日付までの休日 (土曜日、日曜日) の数を数えるよう頼みました。 あなたは与えられた最初と最後の日付が土曜日または日曜日に含まれるならば、最初と最後の日付を含めて数えなければなりません。

日付は datetime.date (このモジュールについてはリンク先を読んでください) 型として与えられます。 結果は整数で返します。

入力: 開始と終了の日付がdatetime.date型で与えられる

出力: 休日の数を整数で返す

例:

checkio(date(2013, 9, 18), date(2013, 9, 23)) == 2

checkio(date(2013, 1, 1), date(2013, 2, 1)) == 8

checkio(date(2013, 2, 2), date(2013, 2, 3)) == 2

どのように使われるか: 日付を操作する方法を学ぶのに、今がいい時期です。 ここでのこれらのアイデアはカレンダーアプリ、顧客関係管理ソフトウェア、 自動化したメッセージングスケジューラなど多くのものでしばしば出てきます。

事前条件: start_date < end_date.

from datetime import date, timedelta


def checkio(from_date, to_date):
    count = 0
    d = from_date
    while d <= to_date:  
        if d.weekday() == 5 or d.weekday() == 6:  
            count += 1
        d += timedelta(days = 1)  
    return count
    

#These "asserts" using only for self-checking and not necessary for auto-testing
if __name__ == '__main__':
    assert checkio(date(2013, 9, 18), date(2013, 9, 23)) == 2, "1st example"
    assert checkio(date(2013, 1, 1), date(2013, 2, 1)) == 8, "2nd example"
    assert checkio(date(2013, 2, 2), date(2013, 2, 3)) == 2, "3rd example"

dateオブジェクトの日付の計算の仕方がわからなかった。

pythonでは日付の計算はtimedeltaオブジェクトが受け持つようだ。例えばコンソールでやってみる

from datetime import date
>>> a=date(2013,9,18)
>>> b=date(2013,9,22)
>>> b-a
datetime.timedelta(4)
>>> c = b-a
>>> c
datetime.timedelta(4)
>>> from datetime import timedelta
>>> c + timedelta(1)
datetime.timedelta(5)
>>> c
datetime.timedelta(4)
>>> d= c +timedelta(2)
>>> d
datetime.timedelta(6)
>>> print(d)
6 days, 0:00:00

このようにdateオブジェクトの計算の裏ではtimedeltaオブジェクトが働いている。

class datetime.timedelta(days=0, seconds=0, microseconds=0, milliseconds=0, minutes=0, hours=0, weeks=0)

全てキーワード引数なので、キーワード指定して思い通りの計算ができる。(今回はdaysだから必ずしもキーワードを指定する必要はないけれど…)


他の人のコード

from datetime import date, timedelta

<200b>

<200b>

def checkio(from_date, to_date):

    return len([1 for d in range((to_date - from_date).days + 1)

                if (from_date + timedelta(d)).weekday() in [5, 6]])

weekday == 5 or weekday == 6 とするよりも

weekday in [5,6]としたほうがスマートですね。あとweekday in (5,6)としても良いようだ。

Verify anagrams

An anagram is a type of word play, the result of rearranging the letters of a word or phrase to produce a new word or phrase, using all the original letters exactly once. Two words are anagrams to each other if we can get one from another by rearranging the letters. Anagrams are case-insensitive and don't take account whitespaces. For example: "Gram Ring Mop" and "Programming" are anagrams. But "Hello" and "Ole Oh" are not.

You are given two words or phrase. Try to verify are they anagrams or not.

Input: Two arguments as strings.

Output: Are they anagrams or not as boolean (True or False)

Example:

verify_anagrams("Programming", "Gram Ring Mop") == True

verify_anagrams("Hello", "Ole Oh") == False

verify_anagrams("Kyoto", "Tokyo") == True


How it is used: Anagramming can be a fun way to train your brain, but they have and another application. Psychologists use anagram-oriented tests, often called "anagram solution tasks", to assess the implicit memory. Anagrams are connected to pseudonyms, by the fact that they may conceal or reveal, or operate somewhere in between like a mask that can establish identity. In addition to this, multiple anagramming is a technique sometimes used to solve some kinds of cryptograms.

Precondition: 0 < |first_word| < 100;
0 < |second_word| < 100;
Words contain only ASCII latin letters and whitespaces.


要はアナグラムになっているかどうかを調べて、ブールで返してくださいという問題。


こうやって考えた。

まず大文字、小文字の別は比較の邪魔でしかないのでlower()で小文字にする。そして、ソートする。
次に空白が邪魔なので、これを取り除く作業をする。

できた文字列がそれぞれ等しいならば、アナグラムであるし、そうでなければアナグラムではない。

def verify_anagrams(first_word, second_word):
    first_word = first_word.lower()
    second_word = second_word.lower()
    
    first_word = list(first_word)
    #first_word = first_word.sort()
    first_word.sort()
    first_word = "".join(first_word)
    print(first_word)
    first_word = first_word.strip()
    
    second_word = list(second_word)
    #second_word = second_word.sort()
    second_word.sort()
    second_word = "".join(second_word)
    second_word = second_word.strip()
    
    if first_word == second_word:
        return True
        
    return False

if __name__ == '__main__':
    #These "asserts" using only for self-checking and not necessary for auto-testing
    assert isinstance(verify_anagrams("a", 'z'), bool), "Boolean!"
    assert verify_anagrams("Programming", "Gram Ring Mop") == True, "Gram of code"
    assert verify_anagrams("Hello", "Ole Oh") == False, "Hello! Ole Oh!"
    assert verify_anagrams("Kyoto", "Tokyo") == True, "The global warming crisis of 3002"

ちなみに一回しんだ。list.sort()でソートしたものが返ってくるんだから、これをそのまま変数に入れればいいや。
と思ったらNoneが返ってきて死んだでこざる。

list.sort()はインプレースにソートしてくれてNoneを返すんだね。

list.sorted()とは違うんだよなぁ…。
知りませんでした。

単純な昇順のソートはとても簡単です: sorted() 関数を呼ぶだけです。そうすれば、新たにソートされたリストが返されます:
>>>

>>> sorted([5, 2, 3, 1, 4])
[1, 2, 3, 4, 5]

リストの list.sort() メソッドを呼びだしても同じことができます。この方法はリストをインプレースに変更します(そして sorted との混乱を避けるため None を返します)。多くの場合、こちらの方法は sorted() と比べると不便です- ただし、元々のリストが不要な場合には、わずかですがより効率的です。
>>>

>>> a = [5, 2, 3, 1, 4]
>>> a.sort()
>>> a
[1, 2, 3, 4, 5]

違いは他にもあります、 list.sort() メソッドはリストにのみ定義されています。一方 sorted() 関数は任意のイテラブルを受け付けます。
>>>

>>> sorted({1: 'D', 2: 'B', 3: 'B', 4: 'E', 5: 'A'})
[1, 2, 3, 4, 5]


ちゃんとチュートリアルに書いてあるし…。しかし、pythonは親切というか生真面目な言語だと感じるなぁ。

さて、一位の人は。


def verify_anagrams(a,b):

    return sorted(a.lower().replace(' ','')) == sorted(b.lower().replace(' ',''))


あっ、replace関数あるじゃん…。
はるかにわかりやすい…。


文字列の変換。空白文字も文字なんだから変換できるじゃん。まわりくどいことする必要なかった。

まーた一つ賢くなってしまいましたなぁ。

How to find friends

Sophiaのドローンは、魂のない愚かなドローンではありません。彼らは友達を作ったり持つことができます。 実際、彼らは既にドローンのための彼ら自身のソーシャルネットワークが動いています! Sophiaはドローン達の間のつながりに関するデータを受け取って、彼らの間の関係について知りたいと思います。

我々はドローン達の間の直接のつながりの配列を持っています。 各々のつながりはハイフンで区切られた友達の二つの名前をもつ文字列で表現されています。 例えば: "dr101-mr99"はdr101とmr99が友達であることを意味しています。 あなたはドローン達の間のもっと複雑な関係を決定できるような関数を書かなくてはいけません。 さらに、あなたには二つの名前が与えられます。 任意の深さの共通のかかわりを介して彼らが関係しているかどうか決定してみましょう。 例えば: 二つのドローンが共通の友人をもつかどうか、もしくは友達がさらに共通の友人をもつか等です。

network

例を見てみましょう:
scout2とscout3は共通の友人scout1を持っているので彼らには関係があります。 superとscout2はsscout、scout4、scout1を経由して関係があります。 しかしdr101とsscoutは関係がありません。

入力: 三つの入力変数: 友達に関する情報、文字列のタプル; 一つ目の名前、文字列; 二つ目の名前、文字列

出力: これらのドローン達に関係があるかどうか、ブール値

例:

check_connection(

("dr101-mr99", "mr99-out00", "dr101-out00", "scout1-scout2",

"scout3-scout1", "scout1-scout4", "scout4-sscout", "sscout-super"),

"scout2", "scout3") == True

check_connection(

("dr101-mr99", "mr99-out00", "dr101-out00", "scout1-scout2",

"scout3-scout1", "scout1-scout4", "scout4-sscout", "sscout-super"),

"dr101", "sscout") == False


どのように使われるか: このコンセプトは結合ネットワークの構築であまり明確でない関係を発見するのに役に立つでしょう。 そしてどのようにソーシャルネットワークが働くかについても。

事前条件: len(network) ≤ 45
if "name1-name2" in network, then "name2-name1" not in network
3 ≤ len(drone_name) ≤ 6
first_name and second_name in network.

network = list(network)
network = '-'.join(network)
network = network.split('-')
network_set = set(network)

ネットワークは相互的だから、ひたすらに集合に入れればいいのかと思った。しかし、これは一応は動くんだけど、テストの3つ目(異なるネットワークがある場合)にはうまく機能しない。

わからんから、答えみて考える。


def check_connection(network, first, second):

    setlist = []

    for connection in network:

        s = ab = set(connection.split('-'))  

        # unify all set related to a, b

        for t in setlist[:]: # we need to use copy  

            if t & ab:       # check t include a, b 

                s |= t 

                setlist.remove(t)

        setlist.append(s)    # only s include a, b

    return any(set([first, second]) <= s for s in setlist)

<200b>

<200b>

<200b>

if __name__ == '__main__':

    #These "asserts" using only for self-checking and not necessary for auto-testing

    assert check_connection(

        ("dr101-mr99", "mr99-out00", "dr101-out00", "scout1-scout2",

         "scout3-scout1", "scout1-scout4", "scout4-sscout", "sscout-super"),

        "scout2", "scout3") == True, "Scout Brotherhood"

    assert check_connection(

        ("dr101-mr99", "mr99-out00", "dr101-out00", "scout1-scout2",

         "scout3-scout1", "scout1-scout4", "scout4-sscout", "sscout-super"),

        "super", "scout2") == True, "Super Scout"

    assert check_connection(

        ("dr101-mr99", "mr99-out00", "dr101-out00", "scout1-scout2",

         "scout3-scout1", "scout1-scout4", "scout4-sscout", "sscout-super"),

        "dr101", "sscout") == False, "I don't know any scouts."



まず、split('-')によって、ネットワークのそれぞれのノードをもったリストをつくる。そして、それを更に集合へと変換している。
これを変数sとabにセットする。


次のfor文はsetlistの浅いコピーを作ってこれを操作しているのだけれど、一度目はsetlistの要素数は0なのでとりあえず。先にすすむ。
setlistにsplit('-')で作られたノードの集合を追加する。これでsetlistの中身ができた。


さっき飛ばしたfor文に再び戻ってきた。浅いコピーを作って、そのあとどうしているのだろうか。


t & ab とあるこの&とはなにか。

tとabのどちらの集合にも含まれている要素だけを返す。論理演算子だ。


次に s |= t
s = s | t と等しい式。 | とは集合sと集合tの少なくともどちらか一方に含まれている要素の集合を返す。

そのあとsetlistから集合tを削除している。



つまり、ノードのつながりがある巨大な集合をつくっているのだ。


最後にfirstとsecondを受け取って、さきほど作った集合の中にfirstとsecondがあればfirstとsecondの間にネットワークがあるとわかる。



ただ、この方法ってネットワークが2つまでの場合にしか対応できないような。3つ以上ではうまく探せないのではないか。

def check_connection(edges, first, last):

    path = {first}

    check = 0

    # All points who can be acceseed from "first"

    # are put in path set.

    # At the end end "last" is checked if is in path set.

    while(len(path) > check):

        check = len(path)

        # this part can be easily optimized to avoid 

        # iterating through edges already accepted in path set.

        # I skipped this to keep my solution 

        # clear and easy to understand

        for edge in edges: 

            one, two = edge.split("-")

            if one in path or two in path:

                path.add(one)

                path.add(two)

    else:

        return last in path

他の人の解答。これ、すごいきれい。

Pawn Brotherhood


世の中のほとんどすべての人が昔のゲーム チェスを知っていて、ルールについて基本的な理解はしている。チェスは様々なユニットを持ち、幅広い動きのパターンを持っているので非常に多くのゲームパターンを可能にしている。(例えば、最後のn手での可能なチェス盤面の数) このミッションではチェスのポーンの動きと振る舞いについて調べる。

チェスは2人で行う戦略的ゲームであり、8つの列(1-8の数で示されるランクと呼ばれるものがある)と8つの行(a-hの文字で示されるファイルと呼ばれるものがある)の四角があるチェッカーのゲームボードの上で行う。 それぞれのチェスボードの四角はユニークな座標のペアによって示される ー 文字と数である(例: "a1", "h8", "d6")。 このミッションでは私たちはポーンについてのみ考える。ポーンは隣接した斜め前にある敵の駒を、そのマスに動かすことによって取ることができる。白のポーンにとって前のマスは現在のマスよりも大きい数字の列になる。

ポーンは一般的に弱いユニットだが、私たちはそれらを8つ持っておりポーンの防御壁を築くことができる。 この戦略ではひとつのポーンは別のポーンを守っている。 別のポーンが取れるマスではポーンは安全(safe)である。 私たちはいくつかの白のポーンだけを持っている。 あなたはいくつのポーンが安全であるか探しだすコードを設計しなくてはならない。



あなたは白のポーンが置かれているマスの座標のセット(set)が与えられる。 いくつのポーンが安全であるか数えなくてはならない。

入力 ポーンが置かれている座標が文字列のsetとして与えられる

出力 安全なポーンの数を整数として出力する

safe_pawns({"b4", "d4", "f4", "c3", "e3", "g5", "d2"}) == 6

safe_pawns({"b4", "c4", "d4", "e4", "f4", "g4", "e5"}) == 1


どのように使われるか ゲームのAIにとって重要なタスクの一つがゲームの状態を見積もる能力である。 このコンセプトはどうやってあなたが単純なチェスの図でやれるかを示すだろう。

事前条件
0 < pawns ≤ 8


このコードは動かない

def check_safe(target):
    """
    ななめ4方の座標をかえす
    """
    
    target = list(target)
    alpha = target[0]
    number = target[1]
    result = []
    
    
    if alpha != "a" and number != 8:
        alphas = ord(alpha)
        alphas = chr(alphas+1)
        result.append(str(alphas) + str(number + 1))
    
    if alpha != "h" and number != 8:
        alphas = ord(alpha) + 1
        alphas = chr(alphas)
        result.append(alphas+ str(number + 1))
        
    if alpha != "a" and number != 1:
        alphas = ord(alpha) - 1
        alphas = chr(alphas)
        result.append(alphas + str(number - 1))
    
    if alpha != "h" and number != 1:
        alphas = ord(alpha) + 1
        alphas = chr(alphas)
        result.appned(alphas + str(number - 1))
        
    return result
    
     

def safe_pawns(pawns):
    pawns = list(pawns)
    count = 0
    for x in pawns:
        result = check_safe(x)
        if pawn in result:
            count += 1
        
    return count
        

if __name__ == '__main__':
    #These "asserts" using only for self-checking and not necessary for auto-testing
    assert safe_pawns({"b4", "d4", "f4", "c3", "e3", "g5", "d2"}) == 6
    assert safe_pawns({"b4", "c4", "d4", "e4", "f4", "g4", "e5"}) == 1

ななめ4方のそれぞれの座標を返して、その数を数えようとしたのだけれど、うまく動かなかった。

どうにも元気がないので、ズルだけれど、正答を読みといて勉強する。

def safe_pawns(pawns):
    def get_coord(cell):
        return ord(cell[0])-96, int(cell[1])

 
    def defend_coord(x, y):
        defend = []
        if (x+1 <= 8) and (y+1 <= 8):
            defend.append([x+1, y+1])
        if (x-1 <= 8) and (y+1 <= 8):
            defend.append([x-1, y+1])
        return defend

    board = [[0 for j in range(9)] for i in range(9)]
    pawns, ans = list(pawns), 0

    for pawn in pawns:
        for pwn in defend_coord(*get_coord(pawn)):
            board[pwn[0]][pwn[1]] = 1

    
    for pawn in pawns:
        ans += board[get_coord(pawn)[0]][get_coord(pawn)[1]]
    return ans

clear一位の人たちのコードはハイレベル過ぎて読み解くのが大変なので、なるべく自分の考えに近いコードを。
まず、defend_coord(x,y)

これはあるポーンの座標(x,y)を引数にとって、そのポーンの座標からみて斜め4方の座標をリストに追加して返すようだ。
もちろん、盤上を超えてしまう座標については除外するようにしている。

次にget_coord(cell)
引数であるcellには、"b4"などの座標が入る。

ord関数は

ord(c)

    1 文字の Unicode 文字を表す文字列に対し、その文字の Unicode コードポイントを表す整数を返します。例えば、 ord('a') は整数 97 を返し、 ord('\u2020') は 8224 を返します。これは chr() の逆です。

とある。
つまり、この96を引いている理由はaを1にbを2と変換することで今後の関数に対応しやすくしている。
返り値は(1、4)など、int型を2つ含むタプルだ。

さてリストの内包表記で8*8のリスト(変数はboard)をつくる。pythonで二次元配列をなんというのかは、わからないけどいわゆる二次元配列のリストだ。要素は全て0

多重代入によって、座標のリストと作ると同時にカウンター変数を0に初期化している


あとはforループによって、board上の全てのポーンの斜め4方の座標を1に変更していく。

最後に、それぞれのポーンの座標をループでまわし、もしboard上の座標と一致していればそれがsafe_pawnなので、カウンターに加算する。

Brackets

Input: An expression with different of types brackets as a string (unicode).

Output: A verdict on the correctness of the expression in boolean (True or False).

Example:

checkio("((5+3)*2+1)") == True

checkio("{[(3+1)+2]+}") == True

checkio("(3+{1-1)}") == False

checkio("[1+1]+(2*2)-{3/3}") == True

checkio("(({[(((1)-2)+3)-3]/3}-3)") == False

checkio("2+3") == True


How it is used: When you write code or complex expressions in a mathematical package, you can get a huge headache when it comes to excess or missing brackets. This concept can be useful for your own IDE.

Precondition:
There are only brackets ("{}" "()" or "[]"), digits or operators ("+" "-" "*" "/").
0 < len(expression) < 103

def checkio(expression):
    OPEN = ('(', '{', '[')
    CLOSE = (')', '}', ']')
    
    stack = []
    for alpha in expression:
        if alpha in OPEN:
            stack.append(alpha)
        elif alpha in CLOSE:
            if len(stack) == 0:
                return False
            else:
                last = stack.pop()
                if OPEN.index(last) != CLOSE.index(alpha):
                    return False
    
    if len(stack) != 0:
        return False
    
    return True
    

#These "asserts" using only for self-checking and not necessary for auto-testing
if __name__ == '__main__':
    assert checkio("((5+3)*2+1)") == True, "Simple"
    assert checkio("{[(3+1)+2]+}") == True, "Different types"
    assert checkio("(3+{1-1)}") == False, ") is alone inside {}"
    assert checkio("[1+1]+(2*2)-{3/3}") == True, "Different operators"
    assert checkio("(({[(((1)-2)+3)-3]/3}-3)") == False, "One is redundant"
    assert checkio("2+3") == True, "No brackets, no problem"

これは前にRubyで似たようなのを解いた記憶がある。スタックにどんどん入れていって、調べれば良い。

pythonで定数を扱うなら、リストよりもタプルのほうが高速。
また、指定の値を持つかどうかを調べるときはindex関数を使えばOK.

Building Base

奇妙なことが起こって、私たちは権力者のために理想的なロボットの街を作っています。 この輝かしいロボットの街では、すべての建物は四角ですべての通りは東西南北の美しい格子状に走っています。 私達が初める前にまず完璧な建物を表現するクラスを作ります。

その街のすべての建物は四角でそれらの壁は通りと平行なので、私たちはあらゆる建物を2つの引数(東西の壁の長さ、南北の壁の長さ)と南西の角の座標と建物の高さとして定義することが出来ます。 これらの値は慣習的に正の数字として表すことにします。 座標の原点は南西にあります 、そのため北の座標は南の座標より大きい値を持って終わります。 このミッションを終わらせるため、いくつかの演算が必要になります。 クラスの詳細を見てください。

class Building(south, west, width_WE, width_NS, height=10)

[south, west] に南西の角がある新しい建物のインスタンスを返します、width_WE と width_NS は大きさで、建物の高さは heightです。 "height" は正の数でデフォルト値は10です。

>>> Building(10, 10, 1, 2, 2)
Building(10, 10, 1, 2, 2)
>>> Building(0, 0, 10.5, 2.546)
Building(0, 0, 10.5, 2.546, 10)


corners()

建物の角の座標の辞書を返します。 辞書は以下のキーがあります、 "north-west"、 "north-east"、 "south-west"、 "south-east"。 それらの値は2つの値からなるリストかタプルです。

>>> Building(1, 2, 2, 2).corners()
{"north-west": [3, 2], "north-east": [3, 4], "south-west": [1, 2], "south-east": [1, 4]}


area()

建物の面積を返します。

>>> Building(1, 2.5, 4.2, 1.25).area()
5.25


volume()

建物の容積を返します。

>>> Building(1, 2.5, 4.2, 1.25, 101).volume()
530.25


__repr__()

これは建物の文字列表現です。このメソッドは"print"と"str"に使われます。 以下のような形式で文字列を返します。
"Building({south}, {west}, {width_we}, {width_ns}, {height})"

>>> str(Building(0, 0, 10.5, 2.546))
"Building(0, 0, 10.5, 2.546, 10)"


このミッションではすべてのデータは正しく、値のチェックを実装する必要はありません。

入力 Buildingクラスのステートメントと式

出力 前述のふるまい

どうやって使われるか ここではロボニアの繁栄を達成する最小の機能を持つ単純なクラスを書く方法を学びます。

事前条件 すべてのデータは正しい

class Building:
    """ Defines a building
    """

    def __init__(self, south, west, width_WE, width_NS, height=10):
        self.south = south
        self.west = west
        self.width_WE = width_WE
        self.width_NS = width_NS
        self.height = height

    def corners(self):

        return dict([("north-west",[self.width_NS + self.south,self.west]),
        ("north-east",[self.south + self.width_NS,self.west + self.width_WE]),
        ("south-west",[self.south,self.west]),
        ("south-east",[self.south,self.west+self.width_WE])])

        

    def area(self):
        """ Returns the area of the building
        """
        return self.width_NS * self.width_WE

    def volume(self):
        """ Return the volume of the building
        """
        return self.area() * self.height

    def __repr__(self):
        return ("Building(" + str(self.south) + ", " + str(self.west) + ", "
                + str(self.width_WE) + ", " + str(self.width_NS) + ", "
                + str(self.height) + ")")

if __name__ == '__main__':
    #These "asserts" using only for self-checking and not necessary for auto-testing
    def json_dict(d):
        return dict((k, list(v)) for k, v in d.items())

    b = Building(1, 2, 2, 3)
    b2 = Building(1, 2, 2, 3, 5)
    assert json_dict(b.corners()) == {'north-east': [4, 4], 'south-east': [1, 4],
                                      'south-west': [1, 2], 'north-west': [4, 2]}, "Corners"
    assert b.area() == 6, "Area"
    assert b.volume() == 60, "Volume"
    assert b2.volume() == 30, "Volume2"
    assert str(b) == "Building(1, 2, 2, 3, 10)", "String"

素直に解いた。しかし、selfの使い方が冗長だなぁ。

Clear 1位


from itertools import product

<200b>

class Building(object): # W to E: width, N to S: depth

    def __init__(self, south, west, width, depth, height=10):

        self.south, self.west = south, west

        self.width, self.depth, self.height = width, depth, height

        self.north, self.east = south + depth, west + width

<200b>

    def corners(self):

        pairs = product(('south', 'north'), ('west', 'east'))

        return {'-'.join(p): [getattr(self, d) for d in p] for p in pairs}

<200b>

    def area(self):

        return self.width * self.depth

<200b>

    def volume(self):

        return self.width * self.depth * self.height

<200b>

    def __repr__(self):

        txt = 'Building({0.south}, {0.west}, {0.width}, {0.depth}, {0.height})'

        return txt.format(self)


self.__init__ で多重代入を行っている。

また、北と東の座標はなんども使うので前もって計算している。これだと確かに無駄がない。

productという関数は、itertoolsの関数みたいで色々便利なものがあるようだ。
qiita.com