AWSでSplunk

1.インスタンスの起動

AWS Marketplaceでsplunkと検索、Splunk Enterpriseを選択して起動
セキュリティグループはデフォルトで適切なものが選ばれている。

2.ログイン

http://IPアドレス:8000 にアクセス

ログインは、
ID:admin
PW:SPLUNK-インスタンスID (例)SPLUNK-i-05c27axxx

3.初期設定

ログインしたら、初期設定をしよう。
1)タイムゾーン
Administators > 基本設定 > タイムゾーンをGMT+9に
2)パスワード
Administators > アカウント設定
 ここで、パスワードを変更しよう。

4.ユーザの作成

設定>ユーザ で作成する。

5.Logのインポート

設定 > データの追加 > アップロード
ファイルの選択 > 次へ
var/log/secure の場合、ソースタイプを「linux_secure]
次へ > 右上の確認 > 実行 > サーチ開始

6.他のappを追加

今回は以下を入れた。無償だけど、ライセンスが必要
Splunk Add-on for Unix and Linux

モンテカルロシミュレーション

モンテカルロシミュレーション

(1)モンテカルロ法とは

確率的シミュレーション。ランダム法ともいわれる。乱数を用いて、かつ、それを繰り返すことで、法則や統計値を見積もること。たとえば、天気予報の降水確率70%などという計算は、雲の動きなどを予想して確率的に計算するモンテカルロ法を使っていると言える。天気予報は1回だけに限れば外れるかもしれないが、繰り返して統計的に考えると、確率値を求めることができる。
シミュレーションはすべてモンテカルロ法とは限らず、不確実性が発生する場合に限定されるが、不確実性、つまり乱数的に起こる方が、シミュレーションとしては一般的であろう。

(2)事例

モンテカルロ法を使うと、乱数を使って確率計算することで、円周率の計算もできる。
なので、単に予測だけではなく、乱数を使うことで、数学的な計算ができたりする。

→このとき、なぜ乱数を使うのかがポイント。全パターンを計算すればいいという話もあるが、それはとんでもない計算量になる。だから、乱数を使うのが効率的なのである。

(3)乱数は本当に正しい乱数か。

人間が適当に数字を言って乱数を取ろうとしても、偏りがでる。自分でアルゴリズムを作っても、偏りがでるロジックになる危険もある。
⇒疑似乱数、ある程度確立された乱数発生器を使うべきだろう。といっても、PythonやExcelなどでいいと思う。


分散減少法を使うともう少しスマートに計算できる

pythonでシミュレーション

シミュレーションと待ち行列であるが、待ち行列の理解があるといい。
人が窓口に並ぶ、待ち時間や作業時間を計算する。
タスクのスケジュールのシュミレーションも同じで、人をタスクととらえ、窓口をCPUと考えるといいだろう。条件を変えて、どれくらいの時間で処理できるかをシミュレーションできる。

1.環境について

(1)概要
GoogleColabで実施する。
python3.hatenadiary.com

(2)環境の確認とモジュールsimpyのインストール
冒頭に以下を実行する。もしかするとpip3かも
!pip install simpy

#カレンフォルダの確認
!pwd
#ファイルの確認
!ls
#インストールされているモジュールを確認
!pip list
#simpyのモジュールを入れる
!pip install simpy 

2.simpy

(1)simpyについて

Pythonで離散イベントシミュレーションを実施できる。
概要としては、simpyをインポートし、env = simpy.Environment() というオマジナイでシミュレーション環境を構築する。そして、ジェネレータをdefで作成し、yieldで次々と送る。
それを実行する。

(2)シンプルなコード1

qiita.com
上記にあるように、以下が、最も単純な書き方であり、文法でもある。

❶プログラム概要
・今の時刻(秒)を表示する。※おそらく、プログラムを開始してからの秒数
・func1(env)のイテレータで、10秒まで回す。

❷プログラムそのもの
上記のサイトそのままである。サイト管理者さん、勉強させてもらっております。ありがとうございます。

import simpy

def func1(env):
    while True:
        print(env.now)
        yield env.timeout(2)

env = simpy.Environment()
env.process(func1(env))
env.run(until=10)  # 0 2 4 6 8

以下、説明を入れてみる。

#simpyをインポート
import simpy

#イテレータを定義する。この中にシミュレーションする内容
を記載する。
def func1(env):
    while True:
        #今の時刻を表示?
        print(env.now)
        #2秒間待つ
        yield env.timeout(2)

#インスタンス化する。シミュレーション環境を作っている?
env = simpy.Environment()

#上記で作成したシミュレーション環境に、プロセスを追加する?
env.process(func1(env))

#実行する
#untilによって、ある時刻か事象になるまでシミュレーションを実行
env.run(until=10)  # 0 2 4 6 8
(3)シンプルなコード2 :計算式を入れる。時間と距離の計算

・秒速20mで進む場合、何m進んだかを表示する。
・func1(env)のイテレータで、5秒まで回す。

import simpy

def func1(env):
    while True:
        #秒速20なので、今の時刻(経過した秒)と20を掛ける
        print(env.now, "秒後の距離=", env.now*20)  
        yield env.timeout(1)

env = simpy.Environment()
env.process(func1(env))
env.run(until=5) 

実行結果は以下

0 秒後の距離= 0
1 秒後の距離= 20
2 秒後の距離= 40
3 秒後の距離= 60
4 秒後の距離= 80
(3)シンプルなコード3:乱数を加える

・先のプログラムに乱数を加える。秒速20mではなく、乱数を掛ける
・進む場合、何m進んだかを表示する。

import simpy

def func1(env):
    mean=20 #平均
    std=2 #標準偏差
    speed=np.random.normal(mean,std) #平均と標準偏差を使って速度の乱数を作る
    while True:
        #秒速20に乱数を付与。
        print(env.now, "秒後の距離=", env.now*speed)  
        yield env.timeout(1)

env = simpy.Environment()
env.process(func1(env))
env.run(until=5) 

実行結果は以下

0 秒後の距離= 0.0
1 秒後の距離= 23.22936762721028
2 秒後の距離= 46.45873525442056
3 秒後の距離= 69.68810288163084
4 秒後の距離= 92.91747050884112
(4)シンプルなコード4:毎回乱数を使う

・先と同様に乱数を使うが、1秒ごとに乱数の値を変える。
・locationで現在位置を指定し、そこに乱数を加えていく

import simpy

def func1(env):
    mean=20 #平均
    std=2 #標準偏差
    location=0 #位置
    while True:
        step=np.random.normal(mean,std) #乱数により、毎回進む距離を発生させる
        print(env.now, "秒後の距離=", location, "今回進む距離=",step)  
        location = location + step #現在位置(location)に、今回進む距離stepを加える
        yield env.timeout(1)

env = simpy.Environment()
env.process(func1(env))
env.run(until=5) 

実行結果は以下

0 秒後の距離= 0 今回進む距離= 18.23077711331148
1 秒後の距離= 18.23077711331148 今回進む距離= 17.822299668852168
2 秒後の距離= 36.05307678216364 今回進む距離= 17.87092879869479
3 秒後の距離= 53.924005580858434 今回進む距離= 22.18420721884411
4 秒後の距離= 76.10821279970254 今回進む距離= 21.176451647274014
(5)シンプルなコード5:何回も繰り返す

・ほしいのは結果だけなので、4秒後の距離の結果だけを出力する。
・それを繰り返す。今までとはenv.run(until=5) の使い方が違う。これまでは、秒を変えることに使っていたが、今回は繰り返しの回数に使う

import simpy

def func1(env):
    mean=20 #平均
    std=2 #標準偏差
    while True:
        location=0 #位置
        for i in range(5):
          step=np.random.normal(mean,std) #乱数により、毎回進む距離を発生させる
          location = location + step #現在位置(location)に、今回進む距離stepを加える
        print(location)
        yield env.timeout(1)

env = simpy.Environment()
env.process(func1(env))
env.run(until=5) #5回繰り返す

実行結果は以下

102.07861590133939
100.20338184303091
92.8811368947726
105.10484325211485
99.95588434026536
(6)シンプルなコード6:工程ごとの時間を乱数で変えて、何回も繰り返す

・工程の作業時間を与え、合計時間を求める。
・各工程の作業時間にはバラつきがあるので、乱数にてバラつきを付与する。
・このとき、バラつきは工程によって違うので、標準偏差を変える

import simpy

def func1(env):
    #標準偏差
    std_L=3 #バラつきが多い工程
    std_S=1 #バラつきが少ない工程

    #工程の時間
    b_time=20  #工程B
    c_time=15  #工程C
    d_time=150  #工程D
    while True:
        total_time = np.random.normal(b_time,std_L) + np.random.normal(c_time,std_L) + np.random.normal(d_time,std_S)  #総時間を乱数を使って求める
        print(total_time)
        yield env.timeout(1)

env = simpy.Environment()
env.process(func1(env))
env.run(until=5) #5回繰り返す

実行結果は以下

190.39466291002768
187.3850570612345
186.02605962989372
189.05992209146984
190.75738535763048
(7)classにしてみる

・中身はよくわかっていないが、classにしてみた

import simpy
import numpy as np

class Func1:
   
    def __init__(self, env):    #コンストラクタ
        self.env = env

        #標準偏差
        self.std_L=3 #バラつきが多い工程
        self.std_S=1 #バラつきが少ない工程

        #工程の時間
        self.b_time=20  #工程B
        self.c_time=15  #工程C
        self.d_time=150  #工程D

    def start(self):   #startメソッドを作成し、時間を計算する
        while True:
          total_time = np.random.normal(self.b_time,self.std_L) + np.random.normal(self.c_time,self.std_L) + np.random.normal(self.d_time,self.std_S)  #総時間を乱数を使って求める
          print(total_time)
          yield env.timeout(1)

env = simpy.Environment() #simpyを作る環境
instance1 = Func1(env) #インスタンスを作成、同時にコンストラクタが呼ばれる
env.process(instance1.start()) #クラスFunc1のインスタンスinstance1のメソッドであるstartを呼び出す。()は引数を渡していないので、何も入れていない。
env.run(until=5) #runで、simpyを実行する。5回繰り返す
(8)上記の結果をグラフにする・・・ヒストグラム

シミュレーション結果を配列に入れて、グラフにする。

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

class Func1:
    arr_time = [] #結果を入れる配列を用意
    def __init__(self, env):    #コンストラクタ 最初に読み込まれる

        self.env = env

        #標準偏差
        self.std_L=3 #バラつきが多い工程
        self.std_S=1 #バラつきが少ない工程

        #工程の時間
        self.b_time=20  #工程B
        self.c_time=15  #工程C
        self.d_time=150  #工程D

    def start(self):   #startメソッドを作成し、時間を計算する
        while True:
          total_time = np.random.normal(self.b_time,self.std_L) + np.random.normal(self.c_time,self.std_L) + np.random.normal(self.d_time,self.std_S)  #総時間を乱数を使って求める
          self.arr_time.append(total_time) #配列に結果を入れていく
          yield env.timeout(1)

env = simpy.Environment() #simpyを作る環境
instance1 = Func1(env) #インスタンスを作成、同時にコンストラクタが呼ばれる
env.process(instance1.start()) #クラスFunc1のインスタンスinstance1のメソッドであるstartを呼び出す。()は引数を渡していないので、何も入れていない。
env.run(until=10000) #runで、simpyを実行する。10000回繰り返す

#以下、ヒストグラムの作成
np_arr = np.array(instance1.arr_time) #リストをnp配列に変換した。リストのままでもグラフが描けるのであれば、それでもいいと思う。
fig, ax = plt.subplots() 
ax.hist((np_arr), bins=100)  #100に分割したヒストグラムという意味だと思う。この値を大きくすると、詳細なグラフになる。

f:id:seeeko:20210913224449p:plain
❷ひげ根
以下は、さらにバラつきがある場合のシミュレーションを実施している。勉強していきたい。
https://sinhrks.hatenablog.com/entry/2014/12/14/005604
https://cpp-learning.com/simpy/

3.ガントチャート

(1)やってみよう

以下のplotyを使うと一気に作ってくれる。
plotly.com
環境としては、plotlyのデフォルトのバージョンだと動かないので、以下のバージョンを指定して動かす。

!pip install plotly==4.9.0

以下は、上記のURLの通り。そのまま張り付けるとガントチャートが表示される。

import plotly.express as px
import pandas as pd

df = pd.DataFrame([
    dict(Task="Job A", Start='2009-01-01', Finish='2009-02-28'),
    dict(Task="Job B", Start='2009-03-05', Finish='2009-04-15'),
    dict(Task="Job C", Start='2009-02-20', Finish='2009-05-30')
])

fig = px.timeline(df, x_start="Start", x_end="Finish", y="Task")
fig.update_yaxes(autorange="reversed") # otherwise tasks are listed from the bottom up
fig.show()

f:id:seeeko:20210918121848p:plain

(2)タスクリストをもとにしたガントチャート
"""
プログラムの概要:タスクの前後関係を考慮しながら、最短作業時間を計算する

- タスクリスト(tasks)に、タスクおよび作業時間や前後関係、担当者が割り当てられている
- 1分ごとに、タスクリストの情報から、作業する内容を決めて、while でループさせる。
- 複数のタスクがある場合は上から順に実行する → クリティカルパスを選べない場合がある
- 各担当者が作業済かどうかのフラグを用意する。
- ループごとに、それぞれのタスクが実行可能か判断する。実行不可能な条件は以下。
    - 完了済みである。(残り時間が0)
    - 前工程が残っている。
    - 担当者がすでに作業済み。
- 実行可能なタスクなら、次のふたつの処理を行う。
    - タスクの残り時間を-1。
    - タスクを担当したリソースの「作業したフラグ」を True にする。
- 実行可能なタスクが無ければ終了。
- そしてガントチャートを表示する。
"""
!pip install plotly==4.9.0
!pip list | grep plot

import plotly.express as px
import pandas as pd

# タスクを定義する。ここは人間が作成
# [タスク名, 完了に必要な時間(分), 前工程のタスク名, タスクに必要なリソース(担当者)] 

tasks = [
['A',0,[],['CSIRT']],  
['B',60,['A'],['CSIRT']],
['C',60,['B'],['CSIRT']],
['D',120,['B'],['CSIRT']],
['E',300,['B'],['CSIRT']],
['F',60,['C'],['Sales']],
['G',60,['F'],['Sales']],
['H',360,['D'],['Vender']],
['I',60,['E'],['CSIRT']],
['J',120, ['D', 'H'],['CSIRT']],
['K',60,['G'],['Sales']],
['L',120,['E', 'I', 'J', 'G', 'K'],['CSIRT','Sales']],
['M',120,['L'],['CSIRT']],
['N',60,['M', 'I'],['Exective']],
['O',120,['E', 'J', 'N'],['CSIRT']],
]

#求める総時間(total_time)を初期化
total_time = 0

#リソース(担当者)を,taskから拾う
resources = []
for task in tasks:
    for resource in task[3]:
        resources.append(resource)
resources = list(set(resources))  # 重複を消すために、setにした後、リストに戻す

#前処理:時間が0のものは、他タスクの「前工程」制限を外す
for task in tasks:
    if task[1] == 0:
        for _task in tasks:  # _を付けているのは、_無しのtaskと区別したいから
            if task[0] in _task[2]:
                _task[2].remove(task[0])

# 最後にガントチャートを表示する。
# ガントチャートの元データとなる情報を格納していく list です。
gantt_source = []


#本処理:
i = 0
while 1: #無限ループ。あとの処理で、10000回でSTOP
    worked_flag = {} #各リソースにおいて、その1分間で仕事をしたかのFLAG。初期値は、まだしていない(=False)
    for resource in resources:
        worked_flag[resource] = False

    # タスクを実行
    for task in tasks:
        # 複数のタスクの中で、実行可能なタスクかを判断。実行可能でない場合はcontinueで抜けて、次のtaskへ
        if task[1] == 0:  # 残り時間が0なら実行不可。
            continue  #このforループにおいて、次のtaskに進む
        if task[2]:  # task[2]=前工程。空だったらFalseで、要素が存在すればTrue。前工程がある場合は、実行不可なので、次のタスクへ。
            continue
        someone_is_busy = False  # タスクには複数の担当者が割り当てられていることがある。その担当者(リソース)が、ひとりでも仕事済み(busy)かどうかを判断する。→その場合はタスクを実行不可
        for resource in resources:
            if resource in task[3] and worked_flag[resource] is True:
                # たとえば、CSIRT の担当タスクだけれど、 CSIRT はもう作業済み -> 実行不可。
                # この if に入るということは、誰かひとりが busy ということなので True にする。
                someone_is_busy = True
                break
        if someone_is_busy:
            # 担当者(リソース)が誰かひとりでも忙しい -> 実行不可。
            continue

        # ここに来た(つまり、continueされなかった)ということは、タスクが実行できるということ。このタスクを実行(=残り時間を1分減らす)。
        task[1] -= 1

        # タスクが実行される = ガントチャートに1分の内容を追加。
        gantt_source.append(
            dict(
                Task=task[0],
                Start=total_time,
                Finish=total_time + 1,
                Resource=' & '.join(task[3])),  # 'CSIRT' とか ['CSIRT', 'Sales']を こうする --> 'CSIRT & Sales'
        )

        # タスクが実行されたので、worked_flagを True にする。
        for resource in resources:
            if resource in task[3]:
                worked_flag[resource] = True

    # 完了(残り時間0)したタスクがあれば、他のタスクの「前工程」制限を外す。
    for task in tasks:
        if task[1] == 0:
            for _task in tasks:  # _を付けているのは、_無しのtaskと区別したいから
                if task[0] in _task[2]:
                    _task[2].remove(task[0])

#    print(tasks, total_time)

    # タスクが実行されたのなら、総時間を1分経過させる。
    for resource in resources:
        if worked_flag[resource]:
            # 総経過時間を+1します。
            total_time += 1
            break

    # タスクが実行されなかったのなら、実行できるタスクが無いということで、終了。
    else:
        break

    # 無限ループ防止の保険。
    if i == 10000:
        break
    i += 1

#print(dict(total_time=total_time))

#gantt_sourceを DataFrame にして、 gantt_source にする。
#from pprint import pprint
#pprint(dict(gantt_source=gantt_source))

# グラフを描くためにDataFrame に変換。
df = pd.DataFrame(gantt_source)

# Start と Finish を datetime ではなく int にしたことに対応。
df['delta'] = df['Finish'] - df['Start']
fig = px.timeline(df, x_start='Start', x_end='Finish', y='Task')
# デフォルトでは 'date'
fig.layout.xaxis.type = 'linear'
fig.data[0].x = df.delta.tolist()
fig.update_yaxes(autorange='reversed') #グラフの項目の表示を逆向きにする。この方が見やすいと思う
fig.show()
(3)生産性なども考慮したガントチャート
!pip install plotly==4.9.0
!pip list | grep plot


#タスクをインポートする場合
from google.colab import files
uploaded = files.upload()

import csv
csvfile='tasklist.csv'   #ファイル名とパスを指定
with open(csvfile, encoding='shift_jis') as file1:  #ファイルを開く
    tasks = list(csv.reader(file1))              #csvモジュールでリストに入れる
print(tasks) 

for _task in range(len(tasks)):
  tasks[_task][1] = int(tasks[_task][1])
  tasks[_task][2] = tasks[_task][2].split(',')
  tasks[_task][3] = tasks[_task][3].split(',')
print(tasks) 


"""
プログラムの概要:タスクの前後関係を考慮しながら、最短作業時間を計算する

- タスクリスト(tasks)に、タスクおよび作業時間や前後関係、担当者が割り当てられている
- 1分ごとに、タスクリストの情報から、作業する内容を決めて、while でループさせる。
- 複数のタスクがある場合は上から順に実行する → クリティカルパスを選べない場合がある
- 各担当者が作業済かどうかのフラグを用意する。
- ループごとに、それぞれのタスクが実行可能か判断する。実行不可能な条件は以下。
    - 完了済みである。(残り時間が0)
    - 前工程が残っている。
    - 担当者がすでに作業済み。
- 実行可能なタスクなら、次のふたつの処理を行う。
    - タスクの残り時間を-1。
    - タスクを担当したリソースの「作業したフラグ」を True にする。
- 実行可能なタスクが無ければ終了。
- そしてガントチャートを表示する。
"""

import datetime

import pandas as pd
import plotly.express as px

# タスクをインポートではなく、人間が定義する場合。
# [タスク名, 完了に必要な時間(分), 前工程のタスク名, タスクに必要なリソース(担当者)] 

tasks = [
['A',0,[],['CSIRT']],  
['B',60,['A'],['CSIRT']],
['C',60,['B'],['CSIRT']],
['D',120,['B'],['CSIRT']],
['E',300,['B'],['CSIRT']],
['F',60,['C'],['Sales']],
['G',60,['F'],['Sales']],
['H',360,['D'],['Vender']],
['I',60,['E'],['CSIRT']],
['J',120, ['D', 'H'],['CSIRT']],
['K',60,['G'],['Sales']],
['L',120,['E', 'I', 'J', 'G', 'K'],['CSIRT','Sales']],
['M',120,['L'],['CSIRT']],
['N',60,['M', 'I'],['Exective']],
['O',120,['E', 'J', 'N'],['CSIRT']],
]

"""
tasks = [
    ['A', 10,         [],                        ['CSIRT']],  # noqa: E241 <- flake8 を無視する設定です。
    ['B',  5,         [],                        ['Sales']],  # noqa: E241
     ['C', 15, ['A', 'B'], ['CSIRT', 'Sales', 'Management']],
     ['D', 10,         [],                        ['Sales']],  # noqa: E241
     ['E', 20,      ['D'],                       ['Vender']],  # noqa: E241
]
"""
# print(tasks)

#求める総時間(total_time)を初期化
total_time = 0

#リソース(担当者)を,taskから拾う
resources = []
for task in tasks:
    for resource in task[3]:
        resources.append(resource)
resources = list(set(resources))  # 重複を消すために、setにした後、リストに戻す

# 新しい概念、生産性です。
productivity = {
    'CSIRT': 2,
}
# productivity={'CSIRT';2,'Sales':1,'Vender':1, 'Excective':1}
# 他のリソースをいちいち :1 で書くのは面倒だと思うので
# これ↓で補完します。
for resource in resources:
    productivity.setdefault(resource, 1)

#前処理:時間が0のものは、他タスクの「前工程」制限を外す
for task in tasks:
    if task[1] == 0:
        for _task in tasks:  # _を付けているのは、_無しのtaskと区別したいから
            if task[0] in _task[2]:
                _task[2].remove(task[0])

# 最後にガントチャートを表示する。
# ガントチャートの元データとなる情報を格納していく list。
gantt_source = []

#本処理:
i = 0
while 1: #無限ループ。あとの処理で、10000回でSTOP
    worked_flag = {} #各リソースにおいて、その1分間で仕事をしたかのFLAG。初期値は、まだしていない(=False)
    for resource in resources:
        # CSIRT:2 だとしたら worked_flag[CSIRT] = [False, False] こう書いているのと同じです。
        worked_flag[resource] = [False] * productivity[resource]

    # タスクを実行
    for task in tasks:
        # 複数のタスクの中で、実行可能なタスクかを判断。実行可能でない場合はcontinueで抜けて、次のtaskへ
        if task[1] == 0:  # 残り時間が0なら実行不可。
            continue  #このforループにおいて、次のtaskに進む
        if task[2]:  # task[2]=前工程。空だったらFalseで、要素が存在すればTrue。前工程がある場合は、実行不可なので、次のタスクへ。
            continue
        someone_is_busy = False  # タスクには複数の担当者が割り当てられていることがある。その担当者(リソース)が、ひとりでも仕事済み(busy)かどうかを判断する。→その場合はタスクの実行不可
        for resource in resources:
            # 生産性の高いリソースについては、 [True, True] のようにすべてのフラグが
            # True になっている(all)とき、 is_busy であると判断します。
            if resource in task[3] and all(worked_flag[resource]):  # allは、全ての要素がTrueかどうかを判断 
                # たとえば、CSIRT の担当タスクだけれど、 CSIRT はもう作業済み -> 実行不可。
                # この if に入るということは、誰かひとりが busy ということなので True にする。
                someone_is_busy = True
                break
        if someone_is_busy:
            # 担当者(リソース)が誰かひとりでも忙しい -> 実行不可。
            continue

        # ここに来た(つまり、continueされなかった)ということは、タスクが実行できるということ。このタスクを実行(=残り時間を1分減らす)。

        # この変数は、担当者(リソース)の余力を示します。
        # 余力というのは、 worked_flag の中にある False の数です。
        remaining_power = 1
        # この if は「タスクの担当者がひとり」という意味。
        # NOTE: 仕様のためです。「複数の担当者を持つタスクについては生産性を考慮しなくていい」
        #       複数の担当者を持つなら、これまでどおり担当者の余力は1です。
        if len(task[3]) == 1:
            resources_for_this_task = task[3]  # これは list
            resource_name_for_this_task = resources_for_this_task[0]  # これは str
            # このリソースのフラグの中に、いくつ False があるか、数えています。
            # 上のコメントに書いたとおり、 False の数が余力の数です。
            remaining_power = worked_flag[resource_name_for_this_task].count(False)
        # 余力の数だけ残り時間を減らします。
        for _ in range(remaining_power):
            # いくら余力があっても残り時間が0だったら、
            # これ以上タスクはできません。(人あまりの状態)
            if task[1] > 0:
                task[1] -= 1
            else:
                break

            # タスクが実行されたので、worked_flagを True にする。
            for resource in task[3]:
                # ちなみに、ここまで処理が進んでいる時点で、
                # worked_flag に False があることは保障されています。indexは、最初にFalseの場所を探す
                worked_flag[resource][worked_flag[resource].index(False)] = True

        # タスクが実行される = ガントチャートに1分の内容を追加。
        gantt_source.append(
            dict(
                Task=task[0],
                Start=total_time,
                Finish=total_time + 1,
                Resource=' & '.join(task[3])),  # 'CSIRT' とか ['CSIRT', 'Sales']を こうする --> 'CSIRT & Sales'
        )

        # ここで for 終わり。
        # これはタスクリストの中のひとつのタスクを見終わったということ。
        # HACK: 関数化したりしてネストを短くしたり減らしたりしないと
        #       そろそろバグの温床になると思う。

    # 完了(残り時間0)したタスクがあれば、他のタスクの「前工程」制限を外す。
    for task in tasks:
        if task[1] == 0:
            for _task in tasks:  # _を付けているのは、_無しのtaskと区別したいから
                if task[0] in _task[2]:
                    _task[2].remove(task[0])

#    print(tasks, total_time)

    # タスクが実行されたのなら、総時間を1分経過させる。
    for resource in resources:
        if any(worked_flag[resource]):
            # 総経過時間を+1します。
            total_time += 1
            break

    # タスクが実行されなかったのなら、実行できるタスクが無いということで、終了。
    else:
        break

    # 無限ループ防止の保険。
    if i == 10000:
        break
    i += 1

# 1分刻みをまとめる
gantt_source_2 = []
for achievement in gantt_source:
    # この実績を前の実績と接続したら True になる変数です。
    connected = False
    # 接続可能な実績があるかどうか探します。
    for _achievement in gantt_source_2:
        if _achievement['Task'] != achievement['Task']:
            # このタスクじゃない。
            continue
        if _achievement['Finish'] == achievement['Start']:
            # 前の実績の終わりと、この実績の始まりが同じ -> 接続できる。
            _achievement['Finish'] = achievement['Finish']
            # さきほど定義した「接続したら True になる変数」を True にします。
            connected = True
            break

    # 接続できなかったならば、これは新しく始めたタスクの実績です。
    # dict を追加します。
    if not connected:
        gantt_source_2.append(achievement)
print(gantt_source_2)

# 所要時間(Finish - Start)の列を作っておきます。これはグラフ上の表記用です。
for row in gantt_source_2:
    row['Delta'] = row['Finish'] - row['Start']

# base_timeとして、実行した日の0時を基準にする。
base_date = datetime.datetime.now().replace(hour=0, minute=0, second=0, microsecond=0)
for row in gantt_source_2:
    # plotlyの仕様で、StartとFinishは時刻表記にする。よって、ベース時刻に、StartとFinishのを足す。グラフの表記がどれが見やすいかという観点から、分ではなく、秒に足すことにした
    row['Start'] = base_date + datetime.timedelta(minutes=row['Start'])
    row['Finish'] = base_date + datetime.timedelta(minutes=row['Finish'])
    # datetime 型ではなくて、 str に直します。
    row['Start'] = row['Start'].strftime('%Y-%m-%d %H:%M')
    row['Finish'] = row['Finish'].strftime('%Y-%m-%d %H:%M')

# print(gantt_source_2)
# gantt_source_2.sort()

# # list -> DataFrame にします。
df = pd.DataFrame(gantt_source_2)

print(df)

# # timeline 作ります。
fig = px.timeline(
    df,
    x_start='Start',
    x_end='Finish',
    y='Task',
    color='Resource',
    # ガントチャートのバーのまんなかに DataFrame の列を表示させるときは
    # こう書きます。
    text='Delta',
)
# # 表の並び順を逆にします。
fig.update_yaxes(autorange='reversed')

fig.show()
print(total_time)

4.バラつきを考慮

(1)各種の分布と乱数

いろいろな乱数がある。
https://note.nkmk.me/python-random-randrange-randint/

シミュレーションをするには、論文をしっかり読む必要があるが、たとえば、以下でどうだろうか。

・単純な作業時間
エスカレーションは、在籍するかしないかという単純な2択に左右されたりもするので、正規分布(または2項分布)
import random
random.normalvariate(平均, 標準偏差)

・ベンダの調査時間
解決しない場合には、どれだけでも時間がかかる工程なので、
基本時間6時間の正規分布+指数分布(平均を8時間とした)

total_time = random.normalvariate(6,1)+random.expovariate(1/8)

参考だが、指数分布は、以下
random.expovariate(1/平均値)

これに、各工程において、バラつきの標準偏差などを変えていけばいいのではないか。

(2)バラつきの結果をヒストグラム化して追加
"""
プログラムの概要:タスクの前後関係を考慮しながら、最短作業時間を計算する

- タスクリスト(tasks)に、タスクおよび作業時間や前後関係、担当者が割り当てられている
- 1分ごとに、タスクリストの情報から、作業する内容を決めて、while でループさせる。
- 複数のタスクがある場合は上から順に実行する → クリティカルパスを選べない場合がある
- 各担当者が作業済かどうかのフラグを用意する。
- ループごとに、それぞれのタスクが実行可能か判断する。実行不可能な条件は以下。
    - 完了済みである。(残り時間が0)
    - 前工程が残っている。
    - 担当者がすでに作業済み。
- 実行可能なタスクなら、次のふたつの処理を行う。
    - タスクの残り時間を-1。
    - タスクを担当したリソースの「作業したフラグ」を True にする。
- 実行可能なタスクが無ければ終了。
- 生産性を考慮する。複数人で作業すれば、作業時間は人数で割った時間になる。
 ただし、複数の担当者(CSIRT:2、Sales:1)で実施するタスクは、Salseは1人のままなので、短縮されない。
 このとき、このタスクはCSIRTが一人で実行できるので、もう一人のCSIRTが他のタスクを実行できる
- そしてガントチャートを表示する。
"""

import datetime
import pandas as pd
import plotly.express as px
import numpy as np
import matplotlib.pyplot as plt
import copy

# タスクをインポートではなく、人間が定義する場合。
# [タスク名, 完了に必要な時間(分), 前工程のタスク名, タスクに必要なリソース(担当者),バラつきの指標] 

tasks = [
['A',0,[],['CSIRT'],1],
['B',60,['A'],['CSIRT'],1],
['C',60,['B'],['CSIRT'],1],
['D',120,['B'],['CSIRT'],1],
['E',300,['B'],['CSIRT'],1],
['F',60,['C'],['Sales'],1],
['G',60,['F'],['Sales'],1],
['H',360,['D'],['Vender'],2],
['I',60,['E'],['CSIRT'],1],
['J',120, ['D', 'H'],['CSIRT'],1],
['K',60,['G'],['Sales'],1],
['L',120,['E', 'I', 'J', 'G', 'K'],['CSIRT','Sales'],2],
['M',120,['L'],['CSIRT'],1],
['N',60,['M', 'I'],['Exective'],1],
['O',120,['E', 'J', 'N'],['CSIRT'],1],
]

def time_calc(tasks):
    #求める総時間(total_time)を初期化
    total_time = 0

    #リソース(担当者)を,taskから拾う
    resources = []
    for task in tasks:
        for resource in task[3]:
            resources.append(resource)
    resources = list(set(resources))  # 重複を消すために、setにした後、リストに戻す

    # 生産性
    productivity = {
        'CSIRT': 2,
    }
    # productivity={'CSIRT';2,'Sales':1,'Vender':1, 'Excective':1}などと書くのは、インポートするデータにどんな担当者が入っているか不明なので、以下で一括処理。
    for resource in resources:
        productivity.setdefault(resource, 1)

    #前処理:時間が0のものは、他タスクの「前工程」制限を外す
    for task in tasks:
        if task[1] == 0:
            for _task in tasks:  # _を付けているのは、_無しのtaskと区別したいから
                if task[0] in _task[2]:
                    _task[2].remove(task[0])

    # ガントチャートの元データを格納するlist
    gantt_source = []

    #本処理:
    i = 0
    while 1: #無限ループ。あとの処理で、10000回でSTOP
        worked_flag = {} #各リソースにおいて、その1分間で仕事をしたかのFLAG。初期値は、まだしていない(=False)
        for resource in resources:
            # CSIRT:2 だとしたら worked_flag[CSIRT] = [False, False] こう書いているのと同じ。
            worked_flag[resource] = [False] * productivity[resource]

        # タスクを実行
        for task in tasks:
            # 複数のタスクの中で、実行可能なタスクかを判断。実行可能でない場合はcontinueで抜けて、次のtaskへ
            if task[1] == 0:  # 残り時間が0なら実行不可。
                continue  #このforループにおいて、次のtaskに進む
            if task[2]:  # task[2]=前工程。空だったらFalseで、要素が存在すればTrue。前工程がある場合は、実行不可なので、次のタスクへ。
                continue
            someone_is_busy = False  # タスクには複数の担当者が割り当てられていることがある。その担当者(リソース)が、ひとりでも仕事済み(busy)かどうかを判断する。→その場合はタスクの実行不可
            for resource in resources:
                # 生産性の高いリソースについては、 [True, True] のようにすべてのフラグが
                # True になっている(all)とき、 is_busy であると判断
                if resource in task[3] and all(worked_flag[resource]):  # allは、全ての要素がTrueかどうかを判断するもの。 
                    # たとえば、CSIRT の担当タスクだけれど、 CSIRT はもう作業済み -> 実行不可。
                    # この if に入るということは、誰かひとりが busy ということなので True にする。
                    someone_is_busy = True
                    break  # 誰か一人でもbusyならば、実行不可なので、抜けてその下へ
            if someone_is_busy:
                # 担当者(リソース)が誰かひとりでも忙しい -> 実行不可。
                continue

            # ここに来た(つまり、continueされなかった)ということは、タスクが実行できるということ。このタスクを実行(=残り時間を1分減らす)。

            # この変数は、担当者(リソース)の余力を示す。
            # 余力というのは、 worked_flag の中にある False の数
            remaining_power = 1
            # この if は「タスクの担当者がひとり」という意味。
            # NOTE: 仕様のためです。「複数の担当者を持つタスクについては生産性を考慮しなくていい」
            #       複数の担当者を持つなら、これまでどおり担当者の余力は1です。
            if len(task[3]) == 1:
                resources_for_this_task = task[3]  # これは list
                resource_name_for_this_task = resources_for_this_task[0]  # これは str
                # このリソースのフラグの中に、いくつ False があるか、数えています。
                # 上のコメントに書いたとおり、 False の数が余力の数です。
                remaining_power = worked_flag[resource_name_for_this_task].count(False)
            # 余力の数だけ残り時間を減らします。
            for _ in range(remaining_power):
                # いくら余力があっても残り時間が0だったら、
                # これ以上タスクはできません。(人あまりの状態)
                if task[1] > 0:
                    task[1] -= 1
                else:
                    break

                # タスクが実行されたので、worked_flagを True にする。
                for resource in task[3]:
                    # ちなみに、ここまで処理が進んでいる時点で、
                    # worked_flag に False があることは保障されています。indexは、最初にFalseの場所を探す
                    worked_flag[resource][worked_flag[resource].index(False)] = True

            # タスクが実行される = ガントチャートに1分の内容を追加。
            gantt_source.append(
                dict(
                    Task=task[0],
                    Start=total_time,
                    Finish=total_time + 1,
                    Resource=' & '.join(task[3])),  # 'CSIRT' とか ['CSIRT', 'Sales']を こうする --> 'CSIRT & Sales'
            )

            # ここで for 終わり。
            # これはタスクリストの中のひとつのタスクを見終わったということ。
            # HACK: 関数化したりしてネストを短くしたり減らしたりしないと
            #       そろそろバグの温床になると思う。

        # 完了(残り時間0)したタスクがあれば、他のタスクの「前工程」制限を外す。
        for task in tasks:
            if task[1] == 0:
                for _task in tasks:  # _を付けているのは、_無しのtaskと区別したいから
                    if task[0] in _task[2]:
                        _task[2].remove(task[0])

        # タスクが実行されたのなら、総時間を1分経過させる。
        for resource in resources:
            if any(worked_flag[resource]):
                # 総経過時間を+1します。
                total_time += 1
                break

        # タスクが実行されなかったのなら、実行できるタスクが無いということで、終了。
        else:
            break

        # 無限ループ防止の保険。
        if i == 10000:
            break
    return gantt_source,total_time

arr_time = [] 
for i in range(1000):
    # tasks をそのまま渡すと、 time_calc 関数は tasks データ自体を書き換えるので、コピーを作る。
    tasks_copy = copy.deepcopy(tasks)

    #乱数を加える。乱数のパラメータによって、処理を変える。
    for j in range(len(tasks_copy)):
        if tasks_copy[j][1] != 0:  # 時間が0の場合は何もしない
            if tasks_copy[j][4] == 1:
               tasks_copy[j][1] = round(random.normalvariate(tasks_copy[j][1],5))
            if tasks_copy[j][4] == 2:
                # tasks_copy[j][1] = round(random.normalvariate(tasks_copy[j][1]*0.8,1)+random.expovariate(1/tasks_copy[j][1]/4))  # 時間の0.8倍を正規分布+時間の1/4を平均値とした指数分布
                tasks_copy[j][1] = round(random.normalvariate(tasks_copy[j][1]*0.8,1)+random.expovariate(1/2)*60)  # 時間の0.8倍を正規分布+時間の1/4を平均値とした指数分布

    gantt_source, total_time = time_calc(tasks_copy)
    arr_time.append(total_time) #配列に結果を入れていく

#以下、Total_timeの結果のヒストグラムを作成
np_arr = np.array(arr_time) #リストをnp配列に変換した。リストのままでもグラフが描けるのであれば、それでもいいと思う。
fig, ax = plt.subplots() 
ax.hist((np_arr), bins=100)  #100に分割したヒストグラムという意味だと思う。この値を大きくすると、詳細なグラフになる。

#ここからは、基準となる(乱数無し)のデータのガントチャートを作る
# 1分刻みをまとめる
tasks_copy = copy.deepcopy(tasks)  # 大元のtasksを変えずに、コピーする 
gantt_source, total_time = time_calc(tasks_copy)

gantt_source_2 = []
for achievement in gantt_source:
    # この実績を前の実績と接続したら True になる変数。
    connected = False
    # 接続可能な実績があるかどうか探す。
    for _achievement in gantt_source_2:
        if _achievement['Task'] != achievement['Task']:
            # このタスクじゃない。
            continue
        if _achievement['Finish'] == achievement['Start']:
            # 前の実績の終わりと、この実績の始まりが同じ -> 接続できる。
            _achievement['Finish'] = achievement['Finish']
            # さきほど定義した「接続したら True になる変数」を True にする。
            connected = True
            break

    # 接続できなかったならば、これは新しく始めたタスクの実績。
    # dict を追加します。
    if not connected:
        gantt_source_2.append(achievement)
print(gantt_source_2)

# 所要時間(Finish - Start)の列を作っておく。これはグラフ上の表記用。
for row in gantt_source_2:
    row['Delta'] = row['Finish'] - row['Start']

print(pd.DataFrame(gantt_source_2))

# base_timeとして、実行した日の0時を基準にする。
base_date = datetime.datetime.now().replace(hour=0, minute=0, second=0, microsecond=0)
for row in gantt_source_2:
    # plotlyの仕様で、StartとFinishは時刻表記にする。よって、ベース時刻に、StartとFinishのを足す。グラフの表記がどれが見やすいかという観点から、分ではなく、秒に足すことにした
    row['Start'] = base_date + datetime.timedelta(minutes=row['Start'])
    row['Finish'] = base_date + datetime.timedelta(minutes=row['Finish'])
    # datetime 型ではなくて、 str に直します。
    row['Start'] = row['Start'].strftime('%Y-%m-%d %H:%M')
    row['Finish'] = row['Finish'].strftime('%Y-%m-%d %H:%M')

# # list -> DataFrame にします。
df = pd.DataFrame(gantt_source_2)

print(df)

# # timeline 作る
fig = px.timeline(
    df,
    x_start='Start',
    x_end='Finish',
    y='Task',
    color='Resource',
    # ガントチャートのバーのまんなかに DataFrame の列を表示させる
    text='Delta',
)
# # 表の並び順を逆に。
fig.update_yaxes(autorange='reversed')

fig.show()
print(total_time)

f:id:seeeko:20210925194253p:plain

PerlとCGI

1.概要

(1)諸々

・ファイルの拡張子は .pl
・開発環境はLinuxなどが一般的であろう。
・動かすだけなら、Webのツールでもいい
https://paiza.io/ja/projects/new
・CGIは言語名称ではないので、PerlでもシェルでもC言語でもよい。

(2)LinuxでPerlだけを動かす

あまりケースは多くないと思うが、Webアプリを作るとかではなく、Linuxでperlを単純にプログラム言語として動かしてみよう。AWSで実行した。

#which perlでperlが入っているかを確認しよう。おそらく入っているはず。
which perl #==>/bin/perl

#ファイルを作成する。拡張子はなんでもいいはずだが、わかりやすく.plとする。
cat <<'EOF' > a.pl
#!/bin/perl
print "hello\n";
EOF

#実行権限の付与
chmod +x a.pl

#実行
./a.pl
(3)インストールと設定

CGIを有効にするには次の2つがある。
❶OptionsディレクティブにExecCGIを有効化
・拡張子はcgiにする。
→厳密にはAddHandlerで指定する。
# AddHandler cgi-script .cgiのコメントアウトは消す
また、plという拡張子でもCGIを有効にするには
AddHandler cgi-script .cgi .pl

<Directory "/var/www/html">
 Options Indexes FollowSymLinks ExecCGI

※サブディレクトリでCGIを実行する場合、サブディレクトリごとに設定が必要(だと思う)

❷ScriptAliasディレクティブにCGIプログラムを格納するディレクトリを指定。
この場合、拡張子は何でも良い。
ScriptAlias /cgi-bin/ "/var/www/html"


Options ExecCGI

❸実際に実行するのは以下のような感じ(AWS)

#http インストール
yum -y install httpd

#perl-CGI インストール
yum -y install perl-CGI

#httpd.confの以下を変更
vi /etc/httpd/conf/httpd.conf

# 以下を修正
#   Options Indexes FollowSymLinks
#  ==>  
#   Options Indexes ExecCGI FollowSymLinks
#
#   #AddHandler cgi-script .cgi
#  ==>  
#   AddHandler cgi-script .cgi

#Apache 起動関連
systemctl start httpd
systemctl enable httpd

cd /var/www/html/

#CGIのファイル(たとえばfile.cgi)を作成

#実行権限を付与
chmod +x file.cgi
(4)コメント

#を使う

(5)おまじない

❶PATH
1行目にパスを書くが、パスは以下の方法で確認する。AWSはデフォルトが以下だった。

# which perl
/bin/perl

または、以下かも
/usr/bin/perl

→作成するファイルの1行目には、以下のようにパスを加える。

#!/bin/perl
print "hello\n";

❷文法チェック
冒頭に以下を入れておこう。文法のチェックをしてくれ、警告を出してくれる。

use strict;
use warnings;

2.書いてみよう

❶表示

print("hello"); #==> hello

または

print "hello";

❷改行コード
\n 
タブは \t
❸シングルクォートとダブルクォーテーション
書き方は、以下のように"でくくる

print "hello\n";

変数があっても、ダブルクォーテーションなら変数展開して表示される。

my $a='Taro';
print "My name is $a."; # ==> My name is Taro.

そのままの文字として表示させたければ'(シングルクォートで囲う)

print 'hello\n';

3.変数

・スカラ変数(数字や文字列)、配列変数、ハッシュ変数の3種類がある。
・スカラ変数は$、配列変数は@、ハッシュ変数は%を使う。

(1)スカラ変数

もっとも一般的な変数で、数字や文字を入れる
❶宣言
myを付けて変数の前に$をつける。

my $abc;
$abc = "Hey";

または

my $abc = "Hey";

❷文字や数字を入れる

my $abc = "Hey";
print $abc; # ==> Hey
my $x = 10;
my $y = 20.3;
print $x; # ==> 10

❸演算

print $x; #==>10
print $x * $x; #==>100
print($x * $y);  #==>203

その他、他のプログラムでよく使う以下も記載できる。
たとえば、++ ** -- +=  +-
❹文字列の演算
2つの文字を接続したい場合は「.」でつなぐ。

my $a='Taro';
print "My name is $a" . "-kun"; # ==> My name is Taro-kun.
(2)配列変数

配列変数を使う場合は@を使う
❶宣言と表示
myを付けて変数の前に@をつける。

my @a = (10, 20, 30);
my @b = (1..10,);
my @c = ('Tokyo', 'Osaka', 'Nagoya');
print @a; #==> 102030
print @b; #==> 12345678910
print @c; #==> TokyoOsakaNagoya

❷配列の要素を表示
よくあるやり方。配列の1つ目を取り出すには、数字の0を指定し、なおかつ、スカラー変数の$をつける。ちなみに、@でも表示はできた。

my @a = (10, 20, 30);
print $a[0]; #==> 10
print @a[0]; #==> 10

末尾を取り出すには、添え字を-1にする。

print $a[-1]; #==> 30

❸ハッシュ変数
Pythonの辞書型と思えばいいだろう。例はこんな感じ。

my %c = ("Tokyo"=>10, "Osaka"=>20, "Nagoya"=>30);
print $c{"Tokyo"}; #==> 10

4.条件分岐

if文を使って条件分岐をする。

(1)簡単な例

簡単な条件分岐をやってみる。
❶年齢が20歳以下なら表示

my $age = 20;
if ($age <= 25) {
    print("You can entry");
}

別の書き方もある。1行で書けるので、すっきり。

my $age = 25;
print("You can entry") if ($age <= 25);

❷2つの条件を組み合わせ

my $age = 20;
my $city = "Osaka";

if (($city eq "Osaka") && ($age <= 25)) {
    print("You can entry");
} else {
    print("You can't");
}

ちなみに、pythonと違って、print文のインデントはなくても動いた。
❸3つ以上の条件
elseifを使えばいい。

(2)比較演算子

❶文字列の比較演算子
文字列の比較の場合の記号は、数字の比較の><==などではなく、以下を使う。

演算子 意味
eq equal 同じ
ne not equal 異なる
gt grater than 大きい
lt less than 小さい

❷数字の比較演算子

演算子 意味
== 同じ
!= 異なる
> より大きい
>= 以上
(3)論理演算子
演算子 意味
&& AND andでも可能
|| OR orでも可能
! NOT notでも可能

5.CGIで利用する

(1)はじめに

CGI.pm を使うと便利なので、はじめに呼び出す。$cgiに深い意味はなく、別の名前でもなんでもいい。

use CGI;
my $cgi = new CGI;

パラメータを変数に格納する場合は、以下が例。

my $url =$cgi->param('url');

$cgiというのは、先に定義した$cgi
ここでは、urlにセットされた値を、このperlプログラムのスカラー変数$urlに入れている。

6.ループ処理

for またはwhileで回す
❶forの場合
0~2までを表示する。

for (my $k = 0; $k <3; $k++) {
    print "k=$k\n";    
}

出力結果は以下

k=0
k=1
k=2

❷whileの場合

my $k = 0;
while ($k < 3) {
    print "k=$k\n";
    $k++;
}

7.ファイル処理

いくつかやり方があるようだ。

(1)ファイルをオープンして読み込む

単純な例で紹介する。まず、以下のファイル(file.txt)があるとする。

1:Tokyo
2:Nagoya
3:Osaka

Perlでファイルを開き、1行目と2行目を表示する。まあ、本来はループ処理をさせることであろう。

open (IN,"file.txt");
@abc=<IN>; #@で配列に入れる
close (IN);

print $abc[0];
print $abc[1];

ここで、IN という文字を使っているが、xxxなどの違う文字でもいい。

(2)ファイルに書き込む

・上書き
open (IN,">file.txt");
・追記
open (IN,">>file.txt");

Linuxで各種サーバ

1.shellinabox コマンドラインをブラウザで実行する

(1)概要

・ブラウザで接続し、そこからLinuxのコマンドを打てるようにする。
・セキュリティを保つための踏み台サーバとしても使えるだろう。
・ポート番号は4200であり、https://xxxxx:4200 でアクセスする。

(2)注意点など

ただし、shellinabox(Shell in a box)を入れるということは、SSHでは接続させたくないということだと思われる。
であれば、SSHでのPWログインを拒否しておく(デフォルト)がいいだろう
ポートを変えることもできるが、80で待ち受けるにはnginxが必要な気がする
以下はポートを変えているが、変える必要はない。

(3)インストール
#epelのインストール
amazon-linux-extras install epel -y
#以下は入っているかも
yum -y install openssl
#Shell in a boxをインストール
yum -y install shellinabox
#自動起動の設定
systemctl enable shellinaboxd.service
#サービス起動
systemctl start shellinaboxd.service
(4)ポート番号を変える場合

・4200を8000に変えてみる
・デフォルトの設定(/etc/sysconfig/shellinaboxd)は以下

#PORT=4200

・設定変更の流し込み

#設定ファイルのバックアップ
cp /etc/sysconfig/shellinaboxd /etc/sysconfig/shellinaboxd.org

#ポートを8000に変更
sed -i '/^PORT=4200/ s/PORT=4200/#PORT=4200\nPORT=8000/' /etc/sysconfig/shellinaboxd

#再起動
systemctl restart shellinaboxd.service

統計検定

(1)概要

Pythonとは直接関係がないが、Python+機械学習であったり、Pythonを使ってデータ分析をされる方も多いと思う。また、期待値であったり、分散、それから、Pythonで棒グラフやヒストグラムを書くなどもあるだろう。そんな、統計学の基礎を学べる試験が統計検定である。
統計検定:Japan Statistical Society Certificate

(2)受験方法

2級まではCBTで受験できるので、いつでも受験可能だ。
Odysseyというので受験。
cbt.odyssey-com.co.jp

私は3級の試験を受けて、80/100で合格できた。合格ラインは65点。
f:id:seeeko:20210110101043p:plain
受験費用であるが、学生は学割が利く。

(3)勉強方法

私は統計に関して基本的な知識があった。
❶テキスト
使ったのは、日本統計学会公認の、データ分析の本と、公式問題集(過去問)である。
インターネットでも過去問と解答(解説は無し)が公開されているので、ある程度知識がある人は、過去問だけを解き、余裕で合格できるのであればテキストは不要かと思う。
また、私は統計学入門(東京大学出版会)という有名な本も持っている。入門書らしいが、私には難しく、とてもきれいな状態で保存されてある。(これは統計検定とは無関係に買った本である)。
❷Web
以下のサイトが評判がいいようだ。私は読んでいないが、時間があれば、じっくり見てみたい。
bellcurve.jp

java

1.Javaについて

(1)学習について

身に付けるのは結構大変だと思います。あるところまでのレベルになるまでが大変です。まず、開発環境を作るものまあまあ手間です。以下のサイトだと、開発環境が不要で、ブラウザ上でコンパイルと実行ができるので便利です。学習するだけなら、手軽さを考えても一つの選択肢になると思います。
https://paiza.io/ja

(2)言葉

・JRE(Java Runtime Environment)
 Javaを使うための環境というかソフト。実行するための必要なライブラリも含んでいる
・JDK(Java Development Environment)
 Javaの開発環境。これがないと、javacなどのコマンドによるコンパイルができません
・Javaのアプリケーションとアプレットとサーブレット
 簡単にいうと、Javaのアプリケーションは、OS上で実行されるプログラム。アプレットは、Web上で動くプログラム、サーブレットは、Webサーバ側で動く。アプレットの場合は、端末側にJavaの環境が必要。サーブレットの場合は、端末側にはブラウザさえあればいいので、サーブレットが今の主流。→JSP
・JavaはAndoroidでも利用されており、スマホアプリはJavaで書かれることが多い。→だから、Javaプログラマにニーズが高まっている。

(3)開発環境Eclipseのダウンロードとインストール

❶ダウンロード
http://mergedoc.osdn.jp/
pleiades-4.7.2-java-win-64bit-jre_20171225.zipをダウンロード。
最新版(左上)を選択して次の画面。Windowsの64bit版の、JavaのFullEditionをダウンロード。
かなり容量が大きい(1.7GB)
❷インストール
なぜか7zipでしか解凍できない。7zipで展開
❸起動
eclipseフォルダにeclipse.exeがあるので、それを起動する

(4)Eclipseの使い方

操作方法は、以下がわかりやすい
https://blog.codecamp.jp/eclipse
プログラムを書いて、
メニュー⇒実行→実行→Javaアプリケーション で画面に出力される

以下、自分でもやってみた
❶Javaプロジェクトの作成
「Javaプロジェクトの作成」をクリックして、プロジェクトの名前を付ける(testとした)
※「ファイル」>「新規」>「Javaプロジェクト」でもいい。項目はデフォルトのまま。
※モジュールも作成するか。→作成する必要はない。必要であれば後から作る
(❷パッケージを作成する) ※作る必要は無し。
 クラスをグループ分けする機能。無くてもいいが、作らなくても勝手に作られる。
 一番上のtestというプロジェクトを右クリックして、「新規」でパッケージを作る
❸作成したパッケージを右クリックして、パッケージの直下にClassを作る
→パッケージを作らなかったら、srcフォルダを右クリック、「新規」「クラス」を作る。
 名前だけを付ける。名前は先頭が大文字 →TestClassとした。すると、TestClass.javaというファイルが作成された。
❹作成したClassに対して、ソースを記述する。※追記する

    public static void main(String[] args) {
        System.out.println("Hello");
    }

❺保存する
Ctl+S
❻メニューバーの「実行」>「実行」>「Javaアプリケーション」

Q.mainじゃなくてもいいの?
A.クラス名は何でもいい。mainメソッドがあればいい。なので、以下で実行できる。
ファイル名:TestClass2.java

package test2;

public class TestClass2 {
    public static void main(String[] args) {
        System.out.println("Hello");
    }
}

Q.動かなかった
A.上記の package test2; を勝手に消すと、動かなかった

Q.ファイルをインポートするには?
A.普通にドラッグすればいい

Q.classファイル必要?
A.Eclipseが自動でコンパイルしてくれてClassファイルを作っているので、不要。コマンドで実行する場合は、classファイルが必要。

Q.Eclipseで保存したファイルは、どこに保存されているの?
A.上のバーに表示されているところに保存されている。基本的にはEclipseの同じフォルダ

(5)Javaで作成するプログラムファイルとコンパイル

①ソースファイルの作成
プログラムのソースファイルの拡張子は .java とする
(例) Pg1.java

※クラスの名前とファイル名を同じにする必要あり。

public class Pg1 {

としたら、ファイル名はPg1.javaとする。

②コンパイル
 コンピュータが理解できる言葉に変換するために、コンパイルをする
 このときのコマンドはjavac 

(例)

> javac Pg1.java

 すると、pg1.classという売クラスファイルが作成される。.classのファイルは中間コード(バイトコード)と呼ばれる

③プログラムの実行
 コンパイルしたファイルを実行する。このとき、クラス名だけでよく、拡張子の.classは不要

> java pg1

2.基礎の基礎

(1)とにかく、プログラムを書いて、動かしてみよう!

おまじない系の文字列が多い。
以下のように、classを定義して、mainメソッドが必要であったりするのであるが、以下のサイトだと、オマジナイ系をすでに書いてくれているから便利だ。
https://paiza.io/ja/projects/new

import java.util.*;

public class Main {
    public static void main(String[] args) throws Exception {
        // Your code here!
        
        System.out.println("XXXXXXXX");
    }
}

余分なものを消して、Helloと表示するだけだと以下

public class Main {
    public static void main(String[] args) {
        System.out.println("Hello");
    }
}

これを実行すると、以下のようにHelloと出る。

Hello

ちなみに、pythonと違って、{を使っているため、インデントは適当でもいい。
もう少し複雑?にしてみる。コメントを入れたり、変数を使ったりしている。

public class Main {
 /**
  * hell という文字を表示する
  変数aに入れた値1を表示
  */
 public static void main(String[] args) {
  System.out.println("hello");
  int a=1;
  System.out.println("aの値は、" + a + "です。");
 }
}
(2)もろもろ

・命令を1行にまとめるには、 ; でつなげる。たとえば、int a = 1; int b = 2 ;
・インデントは必要がない。ただ、インデントをしないと見づらい。インデントはソフトに任せた方がいい。
・たまに、以下のように、最後の最後で}の前に空行がある場合があるが、あまり意味はない。ちなみに、paizaは自動で空けてくる。

    }
 
}

❶エスケープシーケンス

\n //改行
\'     //   'の意味。\をつけないと、プログラム上の意味で利用される

❷コメントの書き方

// 1行のコメント
/*
    この間に複数行のコメントを書く
*/

❸プログラムの正常終了
使い方がよくわかっていないが
System.exit(0); 

3.文字列などの表示

(1)System.out.println

・表示するにはSystem.out.printlnを使う
・変数を記載すると値が表示される。
・文字は(と)で囲うに加え、"と"で囲うことで、そのまま表示する。文字や変数をつなぎ合わせるのに+を使う。

  int a=1;
  System.out.println("aの値は、" + a + "です。");

・System.out.printを使うと、最後に改行コードが入らない。つまり、printlnのlnは改行(line)の意味である。まあ、厳密には、改行はLF(Line Feed)だと思う。

4.変数とデータ型

(1)変数とデータ型について

❶型の宣言
・型の宣言は必ず必要。
・なぜ型が必要か。それは、変数ごとに値を格納するメモリ領域を確保するため。メモリ領域にどれだけのサイズを確保するかを決めるために型がある。
たとえば、intは4バイト、longは8バイト、charは2バイト、など。
・型が違うと代入できない。たとえば、int型の変数に3.2の小数を入れることはできない。ただ、逆というか、大きなサイズに小さなサイズの型は入る。たとえば、float型の変数に、整数3を入れることは可能。ただ、実際には3.0などのfloat型に変換されている。

❷変数
・大文字小文字は区別される
・小文字で作成するのが一般的というか慣習。単語をつなげる場合、2つ目は大文字にすることが多い。newDataなど
・変数には、intやchar、double、booleanなどの基本型とString型、配列などの参照型がある。基本型は値がそのまま入っているが、参照型は、データを格納している場所の情報を格納している

(2)数字

整数の型は、int(4バイト) とlong(8バイト)があるが、厳密にはshort(2バイト)とbyte(1バイト)がある。一般的にはintを使いましょう。
※また、Integer型もある。
techacademy.jp

❶int 4バイト整数
・4バイト=32ビットなので、そこそこの大きな数が扱える。1ビットは符号に使うが、31ビットあるので、-2147483648~2147483647の数。
・書き方は以下です。

int n;
n=123;

または

int n = 123;

intは整数型なので、i =5/2 とすると、2.5であるが、小数点は切り捨てられて2になる。
たとえば、以下を実行すると、2が表示される。

public class Main {
    public static void main(String[] args) {
        // Your code here!
        int i=5;
        i=i/2;
        System.out.println(i);
    }
}

❷long 8バイト整数
・符号が1ビットあるが、2の64乗の数が扱える。
・末尾にlongを意味するLまたはlを付ける。小文字lだと1と間違えやすいので、大文字Lがいいだろう。ただ、intで扱える範囲の数であれば、Lを付与しなければintとして扱われるので、エラーにはならない。

long m;
m=123456789L; //末尾にLまたはlをつける。

または、

long m=123456789L;

❸小数
1.234などの小数(浮動小数点)を扱う場合は、float、double型を使う。一般的にはdoubleを使う。floatを使う場合は、longでLを付けたように、最後にF(またはf)をつける。

(3)文字および文字列

・文字(char)と文字列(String)で囲い方が違う。シングルか、ダブルクォーテーションか。
・改行コードは \r 

❶char 文字(1文字) 
文字はシングルクォーテーション(')で囲う

char c;
c= 'A'; // 'で囲う(Stringは")

❷String 文字列  
・先頭のSは大文字
・文字列はダブルクォーテーション(")で囲う

String mojiretsu = "Hello!";
(4)その他

⑤bolean型
boolean型(ブーリアンと読む)は、trueかfalseを表す

5.演算

(1)演算について

・3+5=8という式において、3や5をオペランド、+や=を演算子という。数字の代わりに変数や文字であってもオペランドという。
・リテラルは、まずは「値」のこと、と考えよう。たとえば、x=5の5という値や、x='Hello'のHelloという文字がリテラルである。
リテラルは型を持ち、上の5というのは整数型(?)のリテラルで、Helloというのは文字型(?)のリテラル。
・簡単な演算をやってみよう

public class Main {
    public static void main(String[] args) {
        int a;
        int b;
        a = 10;
        b = a * 5;
        System.out.println("a = " + a + ", b = " + b); // ===> a = 10, b = 50
    }
}
(2)演算子

❶普通の演算子

演算子 内容
+
-
/
% 余り。たとえば、a%3 であれば、aを3で割った余り

❷代入演算子、インクリメント・デクリメント演算子
・以下の上2つは、演算子の右のオペランドを左の変数に代入するから、代入演算子と言われる。
・下2つは、増やしたり減らしたりするので、インクリメント・デクリメント演算子と言われる。

n += 5;  // n=n+5
n -= 5;  // n=n-5
n++;       // n=n+1
n--;         // n=n-1
(3)文字の演算

文字列でも使える。文字を結合することができる。

public class Main {
    public static void main(String[] args) {
        String a;
        a="こん"+"にちは";
        System.out.println(a);
        }
}

※残念ながら、引き算や他の演算はできない・・・。まあ、当然か。

(4)その他

❶大きい値を出力
Math.max命令を使う

public class Main {
    public static void main(String[] args) {
        int a = 10; int b = 20;
        int x = Math.max(a, b);
        System.out.println(x); // ===> 20
    }
}

5.キーボードから値を入力する

書き方はいろいろあるみたいだが、簡略化するためにutil.Scannerをインポートしておこう。

import java.util.Scanner;

以下は、入力した値をnumという変数に入れる場合。

int num = new java.util.Scanner(System.in).nextInt();

※数字の入力なので、 nextInt()を使っている

(1)数字の入力

・数字の入力なので、 nextInt()を使う
・System.out.printlnを使って入力に関する説明をするメッセージを出す
・入力された数字をnumに入れる
・その結果をSystem.out.printlnを使って出力

import java.util.Scanner;

public class Main {
    public static void main(String[] args) {
        System.out.println("好きな数字は?");
        int num = new java.util.Scanner(System.in).nextInt();
        System.out.println("あなたの好きな数字は " + num + " です!");
    }
}

このとき、printlnではなく、printを使うと、改行されないので、カーソルがすぐ横に表示される。このあたりは好みもあるだろう。
では、実行してみよう。
2を入力すると、出力結果は以下。

好きな数字は?
あなたの好きな数字は 2 です!
(2)文字の入力

・Javaの場合は、変数によって命令が変わる。数字(int型)の場合はで、 nextInt()を使ったが、文字(String)の場合は、String型にして、nextInt()の代わりにnext()、またはnextLine()を使う
・以下がそのソースであるが、入力値が文字列なので、 String word にしている。wordは変数の名前。こうしないとエラーになるので、良かったら試してほしい。

import java.util.Scanner;

public class Main {
    public static void main(String[] args) {
        System.out.println("好きな文字は?");
        String word = new java.util.Scanner(System.in).next();
        System.out.println("あなたの好きな文字は " + word + " です!");
    }
}

6. if文による条件分岐

(1)基本構文
条件式 解説
if (n >= 10) nが10以上なら
if (n == 10) 【数字限定】nが10なら。=を2つ 【文字】==の代わりに、equalsを使う
if (n != 10) nが10でないなら
if (moji.equals("終わり") mojiが「終わり」だったら。文字列の場合は==ではなく、.equalsを使う
if (value != false) valueがfalseでなければ

・文字列(String)の場合は、equalsを使う

public class Main {
    public static void main(String[] args) {
        String you="Pa";
        if (you.equals("Pa")) {
            System.out.print("負け");
        } else if(you.equals("Choki")){
            System.out.print("勝ち");
        } else if(you.equals("Gu")){
            System.out.print("アイコ");
        }
    }
}
(2)オプション(論理演算子)
条件式 解説
&& 2つの条件のAND
|| 2つの条件のOR
条件の否定  →(例) if (!n==0) {

たとえば、

 if (age >= 20 && sex.equals("男") {
(3)三項演算子

a>b ? 1:2 a>bであれば1そうでなければ2を返します。

(4)基本構文

❶ifだけ

        if (条件式) {
            命令など;
        } 

たとえば、20歳以上なら「大人」と表示するだけは以下。

public class Main {
    public static void main(String[] args) {
        int age = 35;
        if (age >= 20) {
            System.out.print("大人"); // ====> 大人
        } 
    }
}

❷if else if else

        if (条件式1) {
           命令など;
        } else if(条件式2){
            命令など;
        } else {
            命令など;
        }

たとえば、年齢で大人か子供を判断する場合

public class Main {
    public static void main(String[] args) {
        int age = 15;
        if (age >= 20) {
            System.out.print("大人");
        } else if(0 <= age){
            System.out.print("子供");
        } else {
            System.out.print("正しく入力してください");
        }
    }
}

⇒⇒この場合は書きにくいが、if文をswitchでも書くことができる。
  switch(条件)
 case 条件の値1: //ここはコロン:
   処理;
   break;
  switch(条件)
 case 条件の値2: //ここはコロン:
   処理;
   break;

7. forやwhileによるループ

(1)forによる繰り返し

サンプルは、以下で、iの値を0~4まで表示する。

public class Main {
    public static void main(String[] args) {
        for (int i=0;i<5;i++){
            System.out.println("iの値は" + i);
        }
    }
}

・減らしていく場合は i--

(2)Forによるループの詳細

続きです。
forによる繰り返しを中断、またはスキップする方法があります。
①break 
 breakはswitchでも使いました。
 これを入れると、ループを中断して抜けます。

public class Main {
    public static void main(String[] args) throws Exception {
        for (int i=0;i<5;i++){
            System.out.println("iの値は" + i);
            if (i*10 >= 15) { //ループを抜ける条件としてif文とbreakを使った
                break;
            }
        }
    }
}

②continue
 continueは、1回だけ処理を抜ける場合に使います。ループ処理は継続されます。

(2)while、forを使った繰り返し

簡単な例で、i=1 から5として、順に表示してみましょう。
(1)前判定

public class Main {
    public static void main(String[] args) {
        int i=1;
        while(i<=4){
            System.out.print(i); //改行コードを入れていません。入れる場合はprintln
            i++;
        }
    }
}

実行結果は以下です。

1234

これは前判定で、後判定にすることも可能です。
その場合は、do whileを使います。
(2)後判定
int i=1;
  do{
System.out.print(i); //改行コードを入れていません。
i++;
} while(i<=4);

(3)forを使う場合

public class Main {
    public static void main(String[] args) {
        for (int i=1;i<=4;i++){
            System.out.print(i); 
        }
    }
}

※こっちの方が、スッキリ書けますね。

8. 配列

・どの言語でもある便利な配列。
・要素というか添え字は0から始まる。
・配列に入れることができるのは、同じ型のデータだけ。

(1)配列の宣言

・new によって、初期化するのと同時に、メモリ領域を確保する。このあたりがPythonとは違って面倒ではあるが、メモリ管理をきっちりしているということであろう。配列の個数のよって、確保するメモリが全く違うので、やむを得ないコマンド。たとえば、int型が4つであれば、int(はたしか4バイトだったので)に必要なメモリを確保する。
以下は、3つの要素数の配列を宣言。

int[] data; 
data[]= new int[3];  

※注意点として、配列を宣言するだけのときには、要素数は指定できない。
つまり、int data[3]; とはできない。
または、以下のように1行で書くことも可能

int[] data = new int[3]; 

(2)配列の要素への初期値の代入

以下は、dataという配列に、値を入れて、最後に2つ目の要素を表示した。
書き方はいくつかある。面倒な方法から、最後に単純な書き方を紹介する。
❶一つ一つ書く

public class Main {
    public static void main(String[] args) {
        int[] data;
        data = new int[3];
        data[0] = 10;
        data[1] = 20;
        data[2] = 30;
        System.out.println(data[1]); //----> 20
    }
}

❷省略してみよう
以下のように、データの値を宣言時に指定する。int[3] としていたが、要素数は後ろの数を数えればわかるので書かない。書くと、逆に不整合になる可能性がある。たとえば、4と宣言したのに3つしかないとか。

public class Main {
    public static void main(String[] args) {
        int[] data;
        data = new int[]{10,20,30};
        System.out.println(data[1]); //----> 20
    }
}

❸さらに短く

public class Main {
    public static void main(String[] args) {
        int[] data = new int[]{10,20,30};
        System.out.println(data[1]); //----> 20
    }
}

❹シンプルな書き方
以下はわずか1行で宣言と値のセットを行っている。

public class Main {
    public static void main(String[] args) {
        int[] data = {10,20,30};
        System.out.println(data[1]); //----> 20
    }
}
(3)配列でループ

❶通常の書き方
以下のように、配列の長さの指定をdata.lengthで指定し(今回は3)、要素を表示させる。

public class Main {
    public static void main(String[] args) {
        int[] data = {10,20,30};
        for (int i = 0; i < data.length; i++){
            System.out.println("data[" + i + "] = " + data[i]);
        }
    }
}

結果は以下である。

data[0] = 10
data[1] = 20
data[2] = 30

❷拡張forを使った書き方
以下のようにスッキリ書ける

public class Main {
    public static void main(String[] args) {
        int[] data = {10,20,30};
        for (int value : data){
            System.out.println(value);
        }
    }
}

結果は以下である。

10
20
30
(4)多次元配列

2次元の配列の場合は、以下のイメージ

[0][0] [0][1] [0][2] [0][3]
[1][0] [1][1] [1][2] [1][3]

9. 型変換(キャスト)

・型変換はときに便利である。
❶intをdouble型に
・ (double)a/b とすると、int型の値を割り算してdouble型にできる
例は以下。iはintなので整数。jは小数点なので、double型。型変換をしないと、i/2の結果の小数点が破棄されて2.0となってしまう。

public class Main {
    public static void main(String[] args) {
        int i=5;
        double j=i/2;
        System.out.println(j); // ===> 2.0
        }
}

以下のように、(double)をつけることで、double型に変換している。結果は2.5と正しい値になる。

public class Main {
    public static void main(String[] args) {
        int i=5;
        double j=(double)i/2;
        System.out.println(j); // ===> 2.5
        }
}

❷文字を数字に
・int i = Integer.parseInt(a); //文字列を数字に変換する
以下をみてみよう。aは文字列であるが、intに型変換をしているので、数字として演算される。

public class Main {
    public static void main(String[] args) {
        String a="1";
        int i = Integer.parseInt(a);//文字列を数字に変換する
        System.out.println(i*3); //====> 3
        }
}

10. 乱数

(1)書き方

おまじないとして、1行目に以下を入れよう。

import java.util.Random;

以下のようにすると、kに0-9までの乱数が入る

int k = new Random().nextInt(10);

試しに書いてみる。

import java.util.*;
import java.util.Random;

public class Main {
    public static void main(String[] args) {
        int k = new Random().nextInt(10);
        System.out.print("乱数は" + k + "です");
    }
}

11.コマンドライン引数

main関数に引数を渡すことができます。渡した引数は、argsの配列に格納されます。
※argsは、arguments(引数)という意味。

class Test {
public static void main(String[] args) {
  System.out.print(args[0]);
  System.out.print(args[1]);
 }
}

実行しましょう

> java Test orange apple
orange
apple

12.ファイルのオープン

以下に記載がある。
https://www.sejuku.net/blog/24744
paizaでもファイルを置くことができて、横のタブにファイル名を記載して、以下のようにファイルを置ける。
f:id:seeeko:20210522170137p:plain
上記のコピペであるが、以下のようにすれば、読み込める。

import java.io.File;
import java.io.FileReader;
import java.io.IOException;
 
public class Main {
    public static void main(String[] args) {
        try {
            File file = new File("a.txt");
            if(file.exists()) {
                FileReader filereader = new FileReader(file);
                int data;
                while((data = filereader.read()) != -1) {
                    System.out.print((char) data);
                }
                filereader.close();
            } else {
                System.out.print("ファイルは存在しません");
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

余談だが、入出力処理の例外のIOExceptionを使う場合、tryが必要。だから、上記のソースでtryをなくすとエラーになる。
仮に、tryを無くして書く場合は、以下のように書く throws(投げる)

public class Main {
    public static void main(String[] args) throws IOException {

paiza.ioでも、デフォルトのコードには throws Exception が記載されている。IOExcepitionでなくても、Exceptionでもうまくいくはずだ。
念のため、ソースを書いておく。例外処理が無いので、少しすっきりかける。

import java.io.File;
import java.io.FileReader;
import java.io.IOException;

public class Main {
    public static void main(String[] args) throws IOException {

            File file = new File("a.txt");
            if(file.exists()) {
                FileReader filereader = new FileReader(file);
                int data;
                while((data = filereader.read()) != -1) {
                    System.out.print((char) data);
                }
                filereader.close();
            } else {
                System.out.print("ファイルは存在しません");
            }
    }
}

12.メソッド

・処理を部品化できるので便利である。プログラムの保守性は格段に上がる。
・public class Main{ で始まるのもMainメソッドである。
・これ以降は憶測なので、余談として。
広い意味のメソッドと、狭い意味ではメソッド(=手続き)があると思う。(確証はない)
狭義のメソッドの場合、クラスの中の処理の部分、つまり手続きという意味。広い意味のメソッドは、複数のメソッドも束ねた処理のひとまとまり。クラスで記載されるひとまとまりである。
MainがあることからわかるようにSubもある。サブメソッドを定義できる。このサブメソッドは、Mainから呼び出され、プログラムをまとめた部品となる。

(1)シンプルなメソッドを作ってみましょう。

❶以下のように、文字を表示するプログラムがある。

public class Main {
    public static void main(String[] args) {
        System.out.println("こんちくわ");
    }
}

これを、methodAというメソッド(プログラムの部品)で、処理を分けてみる。
❷メソッドの書き方
これまでと書き方は同じで、先はmainと書いたところに、新しいメソッド名をつけて、()をつける。
その下に、処理を書く(このあたりはこれまでと同じ。

public static void methodA(){
    System.out.println("こんちくわ");
}

❸呼び出す側
mainからメソッドを呼び出す。
以下のように。

    public static void main(String[] args) {
        methodA();
}

❹やってみる
・このとき、mainメソッドの順番を上にする必要はなく、必ずmainメソッドから実行される。

public class Main {
    public static void methodA(){ //呼び出されるメソッド
        System.out.println("こんちくわ");
    }
    public static void main(String[] args) {  //呼び出す側のメインメソッド
        methodA(); //====>こんちくは
    }
}
(2)引数を使う

先のプログラムで、今度は引数を渡す。
以下は、xとyに値を渡す
❶以下のように、文字を表示するプログラムがある。

public class Main {
    public static void main(String[] args) {
        int x = 10;
        int y = 20;
        System.out.println(x);
        System.out.println(y);
    }
}

出力結果は以下

10
20

❷部品にする
ここで、受け取る引数の型を定義する。今回は数字なのでint xとしている。

    public static void methodA(int x) {
        System.out.println(x);
    }

❸部品(メソッド)を使って書き直す

public class Main {
    public static void methodA(int x) {
        System.out.println(x);
    }
    public static void main(String[] args) {
        methodA(10);
        methodA(20);
    }
}
(3)戻り値を返す

これまでは、戻り値が無かったのでvoidを使った。※void は避けるなので、戻り値を避ける(返さない)と考えればいいだろう。
戻り値を返す場合は、voidのとこで、受け取る戻り値の型を宣言する。

実際にやってみるが、流れとしては以下
・Main関数で、数字を2つ与える
・メソッド(methodA)で数字を足す。結果(ans)を戻り値として返す
・main関数では、その値を表示する。

public class Main {
    public static void main(String[] args)  {
        int ans = methodA(10,20);
        System.out.println("sum = " + ans);
    }
    public static int methodA(int x, int y){
        int ans = x + y;
        return ans;
    }
}

methodAを見てもらうとわかるように、戻り値を返しているので、voidではなくintにしている。
もちろん、数字ではなく文字を返すのであればStringにする。

(4)メソッドのオーバーロード

似たようなメソッドだからといって、同じ名前を付けることはできません。
かといって、引数(渡す値)の型や数が違うごとに、新しい名前をつけていると、
プログラムとしては複雑になります。
オーバーロードは引数の型や個数が異なるだけの場合は、同じ名前をつけていいというものです。


13.クラスとメソッド

(1)言葉や決め事など

❶クラス
・オブジェクトに共通する性質を定義したものがクラス。
・言い方を変えてみると、同一のデータ構造(変数)と同一の手続を(メソッド)もつオブジェクトをまとめて表現する。こうしてできたものがクラス。
・違う言い方をしてみると、オブジェクトは、データ(変数)と手続き(メソッド)をまとめたもの。
・クラスを作ることで、int型やString型などに限定されない、新しい型を作ることができる。たとえば、生徒の名前、住所、年齢、成績などを保持するStudentというClassである。
❷インスタンス
・クラスの定義に基づいてインスタンスが生成される
・一つのクラスに対して,複数のインスタンスが対応する。たとえば、生徒(student)というクラスのインスタンスとして、user1,user2などのインスタンスを定義できます。
・「オブジェクト」と「インスタンス」を同じものようにして説明されることがある。たまに、「オブジェクト」と「クラス」も同じもののように説明されることもある。

(2)クラスを作ってみる

・クラス名の先頭は大文字にする
・クラスには、変数(メンバ変数または属性)や操作(メソッド)を記載する
・クラスで定義した変数をフィールドと呼ぶこともある

❶空のクラスを作る

class Student {
}

❷クラスに変数(フィールド)と操作を記載する

class Student {
    String grade = "Hi school"; // nameという文字列を定義
    void hello(){ // メソッドを記載
        System.out.println("こんちくわ");
    }
}

※フィールドが固定の場合は、finalをつける。また、わかりやすいように大文字にする
final int KEN="Tokyo"

❸インスタンス化する
クラスを作ったら、インスタンス化します。
メモリ領域を割り当てるためにnewを使います。

class Student { //この場合はpubicは付けない
    String grade = "Hi-school"; // nameという文字列を定義
    void hello(){ // メソッドを記載
        System.out.println("こんちくわ");
    }
}
public class Main {
  public static void main(String[] args) {
    Student ito; // itoというインスタンスをStudentクラスの中に作る
    ito = new Student();  //メモリ領域が割り当てられていないので、インスタンス化
    System.out.println(ito.grade); //gradeという値を呼び出して表示
    ito.hello(); //helloというメソッドを呼び出して実行
  }
}
(3)クラスとインスタンス

クラスを実体にしたのがインスタンス
インスタンス化(=クラスをインスタンスにする)にはnewを使う

class Fruits {
double size;
}
・・・  {

Fruits orange = new Fruits();
Fruits apple = new Fruits();}

■その他、事例
public class Main {
public static void main(String[] args) {
Main abc = new Main(); //Mainクラスのインスタンスをnewで作成
abc.print("A");//手続き
}
void print(String moji){
System.out.println(moji);
}
}

※シングルクォーテーション’にした場合、つまり obj.show('A') にしたら、以下に変更する。
void show(char sign)

16.例外処理

(1)基本構文

try
catch
finally

(2)まずはやってみる

❶割り算の処理
以下のようにaをbで割るプログラムがある

public class Main {
    public static void main(String[] args) throws Exception {
        int a = 12; int b=3;
        int c;
        c = a / b;
        System.out.println(c);
    }
}

実行すると、4と表示される。
ここで、b=0とすると、以下のように、ゼロで割っているというエラーがでる。

Exception in thread "main" java.lang.ArithmeticException: / by zero
	at Main.main(Main.java:7)

❷例外処理
そこで、例外処理を入れる。上記のArithmeticExceptionというエラーメッセージをcatchで受け取る。

public class Main {
    public static void main(String[] args) throws Exception {
        try{
            int a = 12; int b=0;
            int c;
            c = a / b;
            System.out.println(c);
        } catch(ArithmeticException e) {
            System.out.println("ZeroDivisionError");
            
        } 

    }
}

こうすると、0で割ってもエラーにならず、プログラムとしてきちんと実行される。