nprogram’s blog

気ままに、プログラミングのトピックについて書いていきます

Simple Factory (C#)

Simple Factoryパターンについて

実際にはデザインパターンではありません。プログラミングのイディオムとなります。

クラス図

f:id:nprogram:20181116062243p:plain

実行イメージ

Cheeseピザの材料を準備します。
Cheeseピザを焼きます。
Cheeseピザをカットします。
Cheeseピザを箱に収納します。
[Check Pizza Name] Name : Cheese
[Check Pizza Box]
Cheeseピザを箱に収納します。


Tomatoピザの材料を準備します。
Tomatoピザを焼きます。
Tomatoピザをカットします。
Tomatoピザを箱に収納します。
[Check Pizza Name] Name : Tomato
[Check Pizza Box]
Tomatoピザを箱に収納します。

コードの説明

ピザストアを例に説明します。

ピザを作る(Pizzaクラスのインスタンスの生成)は、Factoryクラス(コード例ではSimplePizzaFactoryクラス)が担当します。

Pizzaクラスは実体がないインタフェースクラスです。

クライアントクラスである(PizzaStores )は、実体(具象Pizzaクラス:CheesePizzaクラス)を参照しません。

ファクトリークラスのみが、具象Pizzaクラスを扱うだた1つのクラスであるべきです。

具象Pizzaクラスのインスタンスの生成もファクトリークラスのみで行うようにすることで、コードのあちこちで具象Pizzaクラスのインスタンスが生成されることを防ぎます。

コード

using System;

namespace Test
{
    class Program
    {
        static void Main(string[] args)
        {
            PizzaStores pizzaStores = new PizzaStores();

            IPizza cheesePizza;

            cheesePizza = pizzaStores.orderPizza("Cheese");

            Console.WriteLine("[Check Pizza Name] Name : {0}", cheesePizza.GetPizzaName());
            Console.WriteLine("[Check Pizza Box] ");
            cheesePizza.Box();

            Console.WriteLine();
            Console.WriteLine();

            IPizza tomatoPizza;

            tomatoPizza = pizzaStores.orderPizza("Tomato");

            Console.WriteLine("[Check Pizza Name] Name : {0}", tomatoPizza.GetPizzaName());
            Console.WriteLine("[Check Pizza Box] ");
            tomatoPizza.Box();

            Console.ReadLine();
        }
    }

    /// <summary>
    /// ファクトリーのクライアントクラス
    /// </summary>
    public class PizzaStores
    {
        SimplePizzaFactory factory = new SimplePizzaFactory();

        public IPizza orderPizza(string type)
        {

            IPizza pizza = factory.createPizza(type);

            if (pizza == null) return null;

            pizza.Prepare();
            pizza.Bake();
            pizza.Cut();
            pizza.Box();

            return pizza;
        }
    }

    /// <summary>
    /// ピザを作成するファクトリークラス
    /// </summary>
    /// <remarks>
    /// このクラスは、具象Pizzaクラス(CheesePizaクラスとか、TomatoPizzaクラス)を参照するアプリケーションで唯一のクラスであるべき
    /// </remarks>
    public class SimplePizzaFactory
    {
        public IPizza createPizza(string type)
        {
            IPizza pizza = null;

            if (type == "Cheese")
            {
                pizza = new CheesePizza(type);

            }
            else if (type == "Tomato")
            {
                pizza = new CheesePizza(type);
            }

            return pizza;
        }
    }

    /// <summary>
    /// 抽象Pizzaクラス (インタフェースクラス)
    /// </summary>
    public interface IPizza
    {
        string GetPizzaName();

        void Prepare();

        void Bake();

        void Cut();

        void Box();
    }

    /// <summary>
    /// 具象Pizzaクラス
    /// </summary>
    public class CheesePizza : IPizza
    {
        public string Name;

        public CheesePizza(string someName)
        {
            this.Name = someName;
        }

        public string GetPizzaName()
        {
            return this.Name;
        }

        public void Prepare()
        {
            Console.WriteLine(this.Name + "ピザの材料を準備します。");
        }

        public void Bake()
        {
            Console.WriteLine(this.Name + "ピザを焼きます。");
        }

        public void Cut()
        {
            Console.WriteLine(this.Name + "ピザをカットします。");
        }

        public void Box()
        {
            Console.WriteLine(this.Name + "ピザを箱に収納します。");
        }
    }

    /// <summary>
    /// 具象Pizzaクラス
    /// </summary>
    public class TomatoPizza : IPizza
    {
        public string Name;

        public TomatoPizza(string someName)
        {
            this.Name = someName;
        }

        public string GetPizzaName()
        {
            return this.Name;
        }

        public void Prepare()
        {
            Console.WriteLine(this.Name + "ピザの材料を準備します。");
        }

        public void Bake()
        {
            Console.WriteLine(this.Name + "ピザを焼きます。");
        }

        public void Cut()
        {
            Console.WriteLine(this.Name + "ピザをカットします。");
        }

        public void Box()
        {
            Console.WriteLine(this.Name + "ピザを箱に収納します。");
        }
    }
}

Template Method パターン [C#]

TemplateMethodパターンとは

Template Method パターンの目的は、ある処理のおおまかなアルゴリズムをあらかじめ決めておいて、
そのアルゴリズムの具体的な設計をサブクラスに任せることである。そのため、システムのフレームワークを構築するための手段としてよく活用される。

抽象メソッドとは、実装を持たず、メソッドの意味(規約)だけを定義したメソッドです。 抽象メソッドの実装は基底クラスでは行わず、派生クラスで行います。
また、抽象クラスとは、 インスタンスを生成出来ないクラスのことで、 継承して使うことを前提としたクラスのことです。

サンプル例

小型トラック(2tトラック)と大型トラック(10tトラック)を作ろうとしたとき、特定の順序ですべて作成してほしい場合に役立ちます。 サンプルコードでは、以下の順序でトラックを作成します。

(1) パーツを購入する (2) パーツを加工する (3) 組み立てる (4) 塗装する

クラス図

抽象クラスは、TemplateMethodMakeCarクラスです。サブクラス(BigTrackクラス、SmallTrackクラス)に具体的な設計を任せます。

f:id:nprogram:20181108064902p:plain

コード

using System;

namespace TemplateMethodOriginal
{
    class Program
    {
        static void Main(string[] args)
        {
            BigTrack BigTrack = new BigTrack();
            BigTrack.createMakeCar();

            SmallTrack SmallTrack = new SmallTrack();
            SmallTrack.createMakeCar();


            // 実行が一瞬で終わって確認できないので、キーの入力を待ちます
            Console.ReadLine();
        }
    }
    public abstract class TemplateMethodMakeCar
    {
        protected abstract void BuyParts();
        protected abstract void MakeParts();
        protected abstract void Assemble();
        protected abstract void PaintBody();

        public void createMakeCar()
        {
            BuyParts();
            MakeParts();
            Assemble();
            PaintBody();
        }
    }

    public class BigTrack : TemplateMethodMakeCar
    {
        override protected void BuyParts()
        {
            Console.WriteLine("大型トラックの部品を購入する");
        }

        override protected void MakeParts()
        {
            Console.WriteLine("大型トラックの部品を加工する");
        }
        override protected void Assemble()
        {
            Console.WriteLine("大型トラックの組み立てる");
        }
        override protected void PaintBody()
        {
            Console.WriteLine("大型トラックの塗装する");
        }
    }

    public class SmallTrack : TemplateMethodMakeCar
    {
        override protected void BuyParts()
        {
            Console.WriteLine("小型トラックの部品を購入する");
        }

        override protected void MakeParts()
        {
            Console.WriteLine("小型トラックの部品を加工する");
        }
        override protected void Assemble()
        {
            Console.WriteLine("小型トラックの組み立てる");
        }
        override protected void PaintBody()
        {
            Console.WriteLine("小型トラックの塗装する");
        }
    }
}

f:id:nprogram:20181028162858p:plain

Python基礎学習 (クラスについて)

クラスについて

Pythonでもクラスはあります。
クラスをインスタンス化して、オブジェクトを生成します。
クラスを生成したときに呼び出されるクラスのコンストラクタ処理(初期化処理)は、'''def init(self):'''の形式で呼び出されます。
__init__()にかかわらず、すべてのメソッドの決まりとして、第一パラメータは、selfと記述します。
これは、メソッド呼び出し時に、呼び出し元のオブジェクトが渡されるというPythonのしきたりによるものです。

また、Pythonでは、C++でいうオーバーロードの仕組みがないため、デフォルト引数を使用してオーバーロードを実現します。

class Car:

    def __init__(self, speed=0, time_span=0, distance=0):
        self.speed = speed
        self.time_span = time_span
        self.distance = distance

    def run(self, speed, time_span):
        self.speed = speed
        self.time_span = time_span
        self.distance = speed * time_span

    def show_speed(self):
        print('Speed : ' + str(self.speed))

    def show_time_span(self):
        print('Speed : ' + str(self.time_span))

    def show_distance(self):
        print('Distance : ' + str(self.distance))


car1 = Car()
car1.run(10, 5)
car1.show_speed()
car1.show_time_span()
car1.show_distance()

car2 = Car()
car2.run(10, 10)
car2.show_speed()
car2.show_time_span()
car2.show_distance()

# 実行結果
# Speed : 10
# Speed : 5
# Distance : 50
# Speed : 10
# Speed : 10
# Distance : 100

クラスの機能を外部から注入する

関数名の付け方についても注意してください。アンダースコアで意味ごとに区切るのと、はじめは動詞から入るといいと思います。

自分の手と相手の手の情報を表示する ⇒ [show] + [your] + [and] + [enemy] + [hand] = show_your_and_enemy_hand

import numpy
import SystemIO

class Janken:
    HANDS = ['グー', 'チョキ', 'パー']
    
    def show_description(self) -> str:
        return "グー : 0, チョキ : 1, パー : 2"
        
        
    def show_your_and_enemy_hand(self, you, enemy):
        return "あなた: {0}, 相手: {1}".format(self.HANDS[you], self.HANDS[enemy])
        
        
    def decide_enemy_hand(self) -> int:
        return numpy.random.randint(0, len(self.HANDS))
    
    def judge_winner(self, you, enemy) -> str:
        result = (you - enemy) % len(self.HANDS)
        
        if result == 0 : 
            return "Draw"
        
        elif result == 1:
            return "Lose"
        else:
            return "Win"


class SystemIO:
    
    def print_message(self, message : str):
        print(message)
        
    
    def input(self) -> str :
        return input()


class GameFlow:
    
    def __init__(self, game, flow_io):
        self.game = game
        self.enemy_hand_number = 0
        self.flow_io = flow_io
        
    
    def set_enemy_hand_number(self, hand_number : int):
        self.enemy_hand_number = hand_number
    
    
    def play(self):
        self.flow_io.print_message(self.game.show_description())
        
        you = int(self.flow_io.input())
        
        enemy = self.game.decide_enemy_hand()
        
        self.flow_io.print_message(self.game.show_your_and_enemy_hand(you, enemy))
            
        self.flow_io.print_message(self.game.judge_winner(you, enemy))
    
    
    def play_fixed(self):
        self.flow_io.print_message(self.game.show_description())
        
        you = int(self.flow_io.input())
        
        self.game.your_and_enemy_hand(you, self.enemy_hand_number)
            
        self.flow_io.print_message(self.game.judge_winner(you, enemy))



game = Janken()
system_io = SystemIO()

flow = GameFlow(game, system_io)

flow.play()

Pythonでシグモイド関数とシグモイド関数の微分をグラフで書く

はじめに

人工知能のアルゴリズムでは、シグモイド関数とシグモイド関数の微分が大切みたいです。

コードをメモっておきます。

なお、以下のコードで、でグラフの見出しを作成することが可能です。 複数のデータ例がある場合に、便利です。

plt.plot(x, y_sig, label="sigmoid")
plt.plot(x, dy_sig, label="d_sigmoid")
plt.legend()

コード

import matplotlib.pyplot as plt
import numpy as np
import math


# シグモイド関数
# y = 1 / (1 * + e^(-x))
def sigmoid(a):
    s = 1 / (1 + e**-a)
    return s


# シグモイド関数の微分
# dy_sig = sigmoid(x) * (1 - sigmoid(x))
def d_sigmoid(a):
    s = sigmoid(a) * (1 - sigmoid(a))
    return s


e = math.e
dx = 0.1

x = np.arange(-10, 10, dx)

y_sig = sigmoid(x)
dy_sig = d_sigmoid(x)

plt.plot(x, y_sig, label="sigmoid")
plt.plot(x, dy_sig, label="d_sigmoid")
plt.legend()
plt.show()

イメージ

f:id:nprogram:20180928081353p:plain

Pythonでファイルからデータを読み込んで、リストに格納する

Pythonでそのままリスト表示

csv形式のテキストファイルを読み込んで、リストに格納して表示するプログラムです。 テキストファイルは以下を用います。

データファイル

Ichiro,100
Jiro,95

ただ単純にリスト表示すると以下のように、バックスラッシュが入っていたり、要素が分けられていない問題が発生します。

実行イメージ

f:id:nprogram:20180926173531p:plain

コード

score_list = []

score_list_file = open("score")

for score in score_list_file:
    score_list.append(score)

score_list_file.close()

print(score_list)

整形してリスト表示する

rstrip関数でバックスラッシュを削除して、split関数でカンマで要素を分割します。

また、今回は、数字があるため、score_listのリストに要素を追加する際は、数字のパラメータについては、int型でキャストしました。

実行イメージ

f:id:nprogram:20180926172340p:plain

コード

score_list = []

score_list_file = open("score")

for score in score_list_file:
    score = score.rstrip()
    score = score.split(",")
    score_list.append([score[0], int(score[1])])

score_list_file.close()

print(score_list)

株価データをプロットして、CSVファイルを出力する方法

はじめに

jsmを使えば、日本の銘柄の株価を簡単に取得できます。

パッケージ・バージョン

jsm : 0.19

コード

import datetime as dt
from pandas import DataFrame
import jsm
import pandas_datareader.data as web
import matplotlib.pyplot as plt

def jpstock(code, start_date, end_date):
    year, month, day = start_date.split("-")
    start = dt.date(int(year), int(month), int(day))
    year, month, day = end_date.split("-")
    end = dt.date(int(year), int(month), int(day))

    print('CSVを出力中...')
    q = jsm.Quotes()
    target = q.get_historical_prices(code, jsm.DAILY, start_date=start, end_date=end)

    date = [data.date for data in target]
    open = [data.open for data in target]
    high = [data.high for data in target]
    low = [data.low for data in target]
    close = [data.close for data in target]
    volume = [data.volume for data in target]
    adj_close = [data._adj_close for data in target]

    Date = date[::-1]
    Open = open[::-1]
    High = high[::-1]
    Low = low[::-1]
    Close = close[::-1]
    Adj = adj_close[::-1]
    Vol = volume[::-1]

    cdf = DataFrame(index=Date)
    cdf.index.name = "Date"
    cdf["Open"] = Open
    cdf["High"] = High
    cdf["Low"] = Low
    cdf["Close"] = Close
    cdf["Adj Close"] = Adj
    cdf["Volume"] = Vol

    cdf.to_csv(code + '.csv')
    print(code + '.csvを出力しました.')

    print('株価データをプロット中...')
    df = DataFrame(index=Date)
    df['Adj Close'] = Adj

    return df

code='8411'
start_date =  '2018-01-01 '
end_date =  '2018-09-01 '

try:
    jstock = jpstock(code, start_date, end_date)
    jstock['Adj Close'].plot()
    plt.show()
except:
     print('データの取得中にエラーが発生しました.')

銘柄コード(8411)の2018年1月1日から9月1日までに株価の推移

f:id:nprogram:20180921041051p:plain

ワーニングの対処

jsmでユーザーワーニングが発生しますが、実行には問題ありません。ワーニングに対処する場合は、以下のように修正してください。

  • 修正前 [util.py]
soup = BeautifulSoup(html)
  • 修正後 [util.py]
soup = BeautifulSoup(html, 'html.parser')

github.com

GUI版も提供されていました

参考記事のほうで、GUI版も提供されていました。 pyqt5というパッケージをインストールする必要があります。 pip install pyqt5

参考記事

以下の記事を参考にしました。

qiita.com

Pythonで株価のスクレイピングをしてみました

はじめに

Pythonで株価を取得してみました。

自分で、スクレイピングのコードを書かなくても、世の中には、その機能を提供するパッケージが複数あるようです。

  • Quandl
  • pandas_datareader
  • jsm

ここでは、pandas_datareaderを用いて、株価を取得してみたいと思います。

コード

import pandas_datareader.data as web
from datetime import datetime

start = datetime(2000, 1, 1)
end = datetime(2018, 9, 17)
df = web.DataReader('8411.JP', 'stooq')
print(df.head())

上記のコードを実行すると、以下の出力結果が得られます。

<コンソール出力結果>

             Open   High    Low  Close     Volume
Date
2018-09-13  192.0  194.3  191.7  193.3   87685600
2018-09-12  192.4  192.5  191.2  192.0  102462000
2018-09-11  192.7  193.0  192.0  192.7   70223700
2018-09-10  192.3  193.7  191.7  192.9   75642400
2018-09-07  192.2  192.6  190.8  192.0  112240600

Pythonを用いて、簡単に株価を取得できました。

自力でも、スクレイピングができるように引き続き調査します。