ようへいの日々精進XP

よかろうもん

アルゴリズムとプログラミング 「第 2 章 条件分岐」の予習とまとめ

これは

放送大学教養学部の「アルゴリズムとプログラミング」という授業で使われる「アルゴリズムとプログラミング」という教材書籍を自分なりにまとめたものです.

第 2 章では, C 言語の条件分岐に関連する if 文や switch 文等の基本的な内容について触れられています. 課題については, リファクタリング的な内容だったのでテストを書いてリファクタリングしてみました.

尚, 本まとめについては, 以下の Github リポジトリで管理しており, 加筆修正はリポジトリのみ行います.

github.com

1. if 文による条件分岐

1.1 if 文

  • if 文を利用することで条件分岐が可能となる

以下の例だと, 条件式が真 (true) である場合, 処理が実行されるが, 偽 (false) である場合には実行されない.

if ( 条件式 )
  条件式が真 true だった場合の処理;

以下, C 言語の比較演算子の例.

比較演算子 比較演算子の意味 比較演算子の意味 (英語)
== 等しい equal to
!= 等しくない not equal to
> 大きい greater than
< 小さい less than
>= 大きいか等しい greater than or equal to
<= 小さいか等しい less than or equal to

複数の命令を実行したい場合には, 以下のように波括弧 (カーリーブランケット: curly brankey) の記号 { } を利用したブロック文を用いる.

if ( 条件式 ) {
  処理 1;
  処理 2;
  ...
  処理 n;
}

1.2 if-else 文

以下の例では, 条件式が真となる場合には, 処理 T が実行され, 偽の場合には処理 F が実行される.

if ( 条件式 )
  処理 T;
else
  処理 F;

複数の命令を実行したい場合には, 以下のようにカーリーブランケットを利用したブロック文を利用する.

if ( 条件式 ) {
  処理 T0;
  処理 T1;
} else {
  処理 F0;
  処理 F1;
}

2. switch 文による条件分岐

  • 式 (expression) の値に応じて多分岐を行う
  • default はどの case にも一致しなかった場合に実行される, default は省略可能, casedefault の順序は任意
  • breke はそれ以降の処理を行わずに switch 文を抜ける
  • if-else 文に置き換えることが出来る
switch ( 式 ) {
  case 定数式 0:
    処理;
    ...
    break;
  case 定数式 1:
    処理;
    ...
    break;
  case 定数式 2:
    処理;
    ...
    break;
  default:
    処理;
    ...
    break;
}

3. 条件演算子

  • 三項演算子 (ternary operator)
  • if-else 文と類似したコードを書くことが可能
  • 条件式が真であれば, 式 T の値を, 偽であれば式 F の値を返すという演算を行う
  • 式 T と式 F は同じ型の値を返す必要がある
条件式 ? 式 T : 式 F 

以下, if-else 文との比較.

// 条件演算子バージョン
result = a > b ? x : y

// if-else バージョン
if (a > b) {
  result = x;
} else {
  result = y;
}

4. goto 文

  • goto 文はコードの位置を移動させることが出来る
  • goto 文はスパゲティコードを作り出してしまう原因となる為, 多用することは推奨されていない
  • 特殊なエラー処理や多重にネストされたループから抜け出す為に使われることが稀にある
  • ループ処理や関数等の制御構造と比較して処理の流れが解りにくくなり, コードの可読性が悪くなる

以下, for 文と goto 文の比較.

# forfor ( i = 0; i < 1024; i++ ) {
  処理;
}

# goto 文
i = 0;
LABEL_A:
  if ( i < 1024 )
    goto LABEL_B;
  処理;
  i ++;
  goto LABEL_A
LABEL_B:

た, 確かに goto 文は可読性悪い...

演習問題

問 2.1

以下のコードを変更し, scanf 関数を用いて変数 x と変数 y の値を, キーボード等の標準入力から入力出来るようにしなさい.

/* code: ex2-2.c */
#include <stdio.h>

int main()
{
  int x, y;

  x = 500;
  y = 700;
  printf ("X = %d\n", x);
  printf ("Y = %d\n", y);

  if (x > y)
    printf ("X is greater than Y.\n");
  else
    printf ("X is less than or equal to Y.\n");

  return 0;
}

このコードをコンパイルして実行してみる.

$ gcc ex2-2.c -o ex2-2
$ ./ex2-2
X = 500
Y = 700
X is less than or equal to Y.

これを以下のように改修する.

/* code: q2-1.c */
#include <stdio.h>

int main()
{
  int x, y;

  printf ("Enter X Value: ");
  scanf ("%d", &x);
  printf ("Enter Y Value: ");
  scanf ("%d", &y);  
  
  printf ("X = %d\n", x);
  printf ("Y = %d\n", y);

  if (x > y)
    printf ("X is greater than Y.\n");
  else
    printf ("X is less than or equal to Y.\n");

  return 0;
}

このコードをコンパイルして実行してみる.

$ gcc q2-1.c -o q2-1
$ ./q2-1
Enter X Value: 3
Enter Y Value: 5
X = 3
Y = 5
X is less than or equal to Y.

$ ./q2-1
Enter X Value: 1000
Enter Y Value: 10
X = 1000
Y = 10
X is greater than Y.

問 2.2

以下のコードを変更し, 変数 grade が小文字のデータに対しても多分岐出来るようにしなさい.

/* code: ex2-3.c */
#include <stdio.h>

int main ()
{
  char grade;

  grade = 'B';

  switch (grade) {
  case 'A':
    printf ("excellent\n");
    break;
  case 'B':
    printf ("good\n");
    break;
  case 'C':
    printf ("fair\n");
    break;
  case 'D':
    printf ("barely passing\n");
    break;
  case 'F':
    printf ("not passing\n");
    break;
  default:
    printf ("ERROR: invalid character\n");
    break;
  }
  printf ("Your grade is %c\n", grade);
  return 0;
}

このコードをコンパイルして実行する.

$ gcc ex2-3.c -o ex2-3
$ ./ex2-3
good
Your grade is B

これを以下のように改修する.

/* code: ex2-3.c */
#include <stdio.h>

int main ()
{
  char grade;

  printf ("Enter grade: ");
  scanf ("%c", &grade);

  switch (grade) {
  case 'A':
  case 'a':
    printf ("excellent\n");
    break;
  case 'B':
  case 'b':
    printf ("good\n");
    break;
  case 'C':
  case 'c':
    printf ("fair\n");
    break;
  case 'D':
  case 'd':
    printf ("barely passing\n");
    break;
  case 'F':
  case 'f':
    printf ("not passing\n");
    break;
  default:
    printf ("ERROR: invalid character\n");
    break;
  }
  printf ("Your grade is %c\n", grade);
  return 0;
}

このコードをコンパイルして実行してみる.

$ gcc q2-2.c -o q2-2
$ ./q2-2
Enter grade: a
excellent
Your grade is a

$ ./q2-2
Enter grade: b
good
Your grade is b

$ ./q2-2
Enter grade: F
not passing
Your grade is F

$ ./q2-2
Enter grade: e
ERROR: invalid character
Your grade is e

問 2.3

以下のコードでは switch 文が使われている. switch 文を使わずに if-else 文で書き換えなさい. 論理演算子 (論理積 &&, 論理和 ||, 否定 !) 等を利用すること.

/* code: q2-3a.c */
#include <stdio.h>

int main ()
{
  int a;
  a = 3;
  switch (a) {
    case 0:
    case 1:
    case 2:
      printf ("A\n");
      break;
    case 3:
    case 4:
      printf ("B\n");
      break;
    default:
      printf ("ERROR: invalid number\n");
      break;
  }

  return 0;
}

このコードをコンパイルして実行してみる.

$ gcc q2-3a.c -o q2-3a
$ ./q2-3a
B

これを以下のように, まずはファイルの分割を行った.

/* code: q23b.c */
#include <stdio.h>
#include "q23b.h"

int main ()
{
  int num;
  num = 1;

  char res;
  res = *foobar(num);
  printf ("%c\n", res);

  return 0;
}

/* code: q23b.h */
#ifndef Q23B_H
#define Q23B_H

char *foobar(int);

#endif

/* code: foobar.c */
# include "q23b.h"

char *foobar(int num){
  switch (num) {
    case 0:
    case 1:
    case 2:
      return "A";
      break;
    case 3:
    case 4:
      return "B";
      break;
    default:
      return "ERROR: invalid number";
      break;
  }
}

その上で, foobar.c をリファクタリングしていく. リファクタリングするにあたって, Unity を使って以下のようなテストコードを書いた. C 言語のユニットテストには Unity がシンプルで良い感じだったので, 別の機会に詳細に掘り下げる予定.

/* code: tests/test_q23b.c */
#include <stddef.h>
#include "vendor/unity.h"
#include "../q23b.h"

void setUp(void)
{
}

void tearDown(void)
{
}

void test_foobar_0(void)
{
   TEST_ASSERT_EQUAL_STRING("A", foobar(0));
}

void test_foobar_1(void)
{
   TEST_ASSERT_EQUAL_STRING("A", foobar(1));
}

void test_foobar_2(void)
{
   TEST_ASSERT_EQUAL_STRING("A", foobar(2));
}

void test_foobar_3(void)
{
   TEST_ASSERT_EQUAL_STRING("B", foobar(3));
}

void test_foobar_4(void)
{
   TEST_ASSERT_EQUAL_STRING("B", foobar(4));
}

void test_foobar_5(void)
{
   TEST_ASSERT_EQUAL_STRING("ERROR: invalid number", foobar(5));
}

int main(void)
{
   UnityBegin("tests/test_q23b.c");

   RUN_TEST(test_foobar_0);
   RUN_TEST(test_foobar_1);
   RUN_TEST(test_foobar_2);
   RUN_TEST(test_foobar_3);
   RUN_TEST(test_foobar_4);
   RUN_TEST(test_foobar_5);

   return (UnityEnd());
}

テストは make で実行出来るように, 以下のような Makefile も用意した.

CFLAGS  = -std=c99
CFLAGS += -g
CFLAGS += -Wall
CFLAGS += -Wextra
CFLAGS += -pedantic
CFLAGS += -Werror

VFLAGS  = --quiet
VFLAGS += --tool=memcheck
VFLAGS += --leak-check=full
VFLAGS += --error-exitcode=1

test: tests.out
        @./tests.out

memcheck: tests.out
        @valgrind $(VFLAGS) ./tests.out
        @echo "Memory check passed"

clean:
        rm -rf *.o *.out *.out.dSYM *.dSYM

tests.out: q23b.c foobar.c tests/vendor/unity.c tests/test_q23b.c
        @echo Compiling $@
        @$(CC) $(CFLAGS) foobar.c tests/vendor/unity.c tests/test_q23b.c -o tests.out

build:
        @$(CC) $(CFLAGS) q23b.c foobar.c -o q23b

試しにテストを走らせてみる.

$ make test
Compiling tests.out
tests/test_q23b.c:47:test_foobar_0:PASS
tests/test_q23b.c:48:test_foobar_1:PASS
tests/test_q23b.c:49:test_foobar_2:PASS
tests/test_q23b.c:50:test_foobar_3:PASS
tests/test_q23b.c:51:test_foobar_4:PASS
tests/test_q23b.c:52:test_foobar_5:PASS

-----------------------
6 Tests 0 Failures 0 Ignored
OK

いい感じ. 以下のようにリファクタリングを行った.

/* code: foobar.c */
# include "q23b.h"

char *foobar(int num){
  if (num == 0 || num == 1 || num == 2) {
    return "A";
  } else if (num == 3 || num == 4) { 
    return "B";
  } else {
    return "ERROR: invalid number";
  }
} 

試しにテストを走らせる.

$ make test
tests/test_q23b.c:47:test_foobar_0:PASS
tests/test_q23b.c:48:test_foobar_1:PASS
tests/test_q23b.c:49:test_foobar_2:PASS
tests/test_q23b.c:50:test_foobar_3:PASS
tests/test_q23b.c:51:test_foobar_4:PASS
tests/test_q23b.c:52:test_foobar_5:PASS

-----------------------
6 Tests 0 Failures 0 Ignored
OK

いい感じ.

以上

感じたことなど.

  • goto 文は出来る限り「使うな」
  • 三項演算子が出てきた (Ruby も C も同じ書き方なんだな)
  • switch 文にて, 条件が複数になる場合 case 文は縦に並べて書く必要があるのは辛い
  • 教材には掲載されていないが, コード分割の雰囲気を掴むことが出来た
  • C のテストはとりあえず Unity で書いていこうと思う (Unity はググらビリティ悪いよな)

2018 年 10 月 06 日 (土)

ジョギング

  • 台風の為, 朝のジョギングはお休み
  • 夕方から山王公園往復, 快調に飛ばすおじさん達が多い印象 (あ, 俺もおじさんか)
  • 懸垂 4 回, だんだん慣れてきた
  • 足のあっちこっちが痛い

日課

  • (腕立て x 50 + 腹筋 x 50) x 3

台風

  • 朝から台風 25 号が直撃していて強い風の轟音とともに午前中は過ごした

豚の生姜焼き

  • 実は豚の生姜焼きはあまり好きではない
  • でも, 奥さんが作る豚の生姜焼きは生姜もしっかり擦ってあって, 豚肉のジューシーさが失われずにいてとても美味しい

今日のトゥウィート

いろいろと勉強になったのでまとめた.

明日は

FTP ユーザーの振る舞いをテストをする rspec-ftp を試した + 抹茶を追加しました

tl;dr

以前に以下のような記事を書きました.

inokara.hateblo.jp

この時には自前の Ruby スクリプトを使って, ftp ユーザーの振る舞い (ログイン出来るか, chroot になっているか, 読み書き, 削除出来るか等) をチェックしていました.

今回, 以下の Gem を拡張して Rspec を介してチェック出来るようにしてみました.

github.com

尚, 自分がチェックしたかった振る舞いに対応するマッチャーが実装されていなかったので, 追加で実装してプルリクエストしています.

github.com

マージされると嬉しいなあ.

FTP ユーザーの振る舞いをテストする

なぜ, FTP ユーザーの振る舞いをテストしたいのか

そもそも, FTP のようないにしへの技術を未だに利用しているのかと突っ込まれそうな気がしていますが, 実際のところ FTP を利用したいというニーズはあります. その中でサーバーの構築というよりは FTP を利用するユーザーの追加や削除という作業に多くの時間を割かれます. 当然, これらの作業はコード化していますが, 作成した FTP ユーザーが正しくログイン出来るか, ファイルの追加や削除は行えるか, 意図しないディレクトリへのアクセスは許可されていないか等を確認した上で依頼主にエビデンスとして共有する必要があると考えています. また, この確認を自作のスクリプトではなく, 既存のテストフレームワーク上で実行することで汎用性を高め, 自作スクリプトという属人化しやすい部分を廃していくことを目的としています.

ということで, 今回は rspec-ftp を利用して FTP サーバーに作成したユーザーをテストする環境を以下のサンプルに用意してみましたので, これを利用して FTP ユーザーの振る舞いテストの雰囲気を紹介致します.

サンプルはこちらから

github.com

サンプル実行

想定する FTP サーバー, FTP ユーザー

  • FTP サーバーは vsftpd を利用
  • FTP サーバーの IP アドレスは 172.26.0.6 (コンテナ間は vsftpd-server という名前でアクセスすることが出来る)
  • パッシブモードで起動し, パッシブポートは 21200 から 21210 ポートを利用
  • FTP ユーザー名は ftpuser, FTP ユーザーパスワードは supersecret

環境構築

docker-compose up -d

以下のように vsftpd サーバーと Ruby 環境が 3 環境起動します.

$ docker-compose up -d
Creating network "rspec-ftp-sample_my_sample_net" with driver "bridge"
Creating rspec-ruby23  ... done
Creating rspec-ruby25  ... done
Creating vsftpd-server ... done
Creating rspec-ruby24  ... done

ユーザー名, パスワードを secret.yml に定義する

以下のように secret.yml を定義します.

vsftpd-server:
  users:
    - username: ftpuser
      password: supersecret

フォーマットは以下の通りです.

IP アドレス又はホスト名:
  users:
    - username: ユーザー名
    - password: パスワード

このファイルはリポジトリにうっかりアップしてしまわないように .gitignore に登録しておくと良いでしょう.

$ cat .gitignore
secret.yml

テストを実行する...その前に

テストは rake コマンドを介して実行することを想定している為, rake -T を実行してタスクの一覧を確認しておきます.

bundle exec rake -T

以下のように出力されることを確認します.

$ docker-compose exec rspec-ruby25 bundle exec rake -T
rake ftpcheck:vsftpd-server:ftpuser  # Run ftpcheck to vsftpd-server by ftpuser

気を取り直して, テスト実行

以下のように rake コマンドを利用してテストを実行します. 一応, Ruby のバージョンに応じて以下のようにコマンドが別れています.

# Ruby 2.5.x 環境で実行する
docker-compose exec rspec-ruby25 bundle exec rake ftpcheck:vsftpd-server:ftpuser

# Ruby 2.4.x 環境で実行する
docker-compose exec rspec-ruby24 bundle exec rake ftpcheck:vsftpd-server:ftpuser

# Ruby 2.3.x 環境で実行する
docker-compose exec rspec-ruby23 bundle exec rake ftpcheck:vsftpd-server:ftpuser

以下のように出力されることを確認します.

$ docker-compose exec rspec-ruby25 bundle exec rake ftpcheck:vsftpd-server:ftpuser
/usr/local/bin/ruby -I/usr/local/bundle/gems/rspec-core-3.8.0/lib:/usr/local/bundle/gems/rspec-support-3.8.0/lib /usr/local/bundle/gems/rspec-core-3.8.0/exe/rspec spec/ftp_spec.rb

#be_accessible (real server)
  can login valid user and password

#be_chroot (real server)
  check chroot enabled

#be_writable (real server)
  check writable with active mode

#be_removable (real server)
  check removable

Finished in 0.09052 seconds (files took 0.18992 seconds to load)
4 examples, 0 failures

# Summary by Type or Subfolder

| Type or Subfolder  | Example count | Duration (s) | Average per example (s) |
|--------------------|---------------|--------------|-------------------------|
| ./spec/ftp_spec.rb | 4             | 0.07874      | 0.01968                 |


# Summary by File

| File               | Example count | Duration (s) | Average per example (s) |
|--------------------|---------------|--------------|-------------------------|
| ./spec/ftp_spec.rb | 4             | 0.07874      | 0.01968                 |

いい感じでテストが PASS しました. ちなみに, chroot が適切に設定されていない場合には...

$ docker-compose exec rspec-ruby25 bundle exec rake ftpcheck:xxx.xxx.xxx.xxx:user1
...
Failures:

  1) #be_chroot (real server) check chroot enabled
     Failure/Error: expect(ENV['TARGET_HOST']).to be_chroot.user(property['username']).pass(property['password'])
       expected "xxx.xxx.xxx.xxx" to be chroot
     # ./spec/ftp_spec.rb:11:in `block (2 levels) in <top (required)>'

Finished in 2.06 seconds (files took 0.20194 seconds to load)
4 examples, 1 failure
...

上記ようにテストはものの見事に失敗します. この時に vsftpd.log を確認すると以下のように上位ディレクトリを参照してしまっていることが確認出来ます.

...
Sat Oct  6 15:02:41 2018 [pid 1410] [user1] FTP command: Client "111.222.333.444", "CWD ../"
Sat Oct  6 15:02:41 2018 [pid 1410] [user1] FTP response: Client "111.222.333.444", "250 Directory successfully changed."
Sat Oct  6 15:02:41 2018 [pid 1410] [user1] FTP command: Client "111.222.333.444", "PWD"
Sat Oct  6 15:02:41 2018 [pid 1410] [user1] FTP response: Client "111.222.333.444", "257 "/home""
Sat Oct  6 15:02:41 2018 [pid 1412] CONNECT: Client "111.222.333.444"
...

意図した通りです. これをずーっとやりたかったんです.

余談

余談という言い方もアレですが

実装するにあたって色々と勉強になったことのメモを書いていきます. 色々と気付きがあったので, 思い出したら追記していきたいと思います.

FTP について

  • アクティブモードとパッシブモードについて理解が曖昧だったので, 改めてこれらについて理解を深める良い機会になった
    • TCP 20 番ポートはデータ転送用, TCP 21 番ポートは制御用
    • アクティブモードはサーバーが TCP 20 番ポートに対してデータの転送を行う
    • パッシブモードはデータ転送ポートは不定で, サーバー側から明示的に範囲を指定された中のポートに対してデータ転送を行う

下図の解説が解りやすかった為, 引用させて頂きました.

r10zu04.gif

インターネット・プロトコル詳説(11):FTP(File Transfer Protocol)~後編 より引用.

抹茶の追加

以前にも rspec のカスタムマッチャの追加方法は勉強しました.

inokara.hateblo.jp

Rspec::Matchersdefine メソッド内に処理を書いていきます. また, メソッドチェーンは chain メソッド内にチェーンしたいメソッドを定義していきます.

Specinfra の property と set_property を利用する

FTP ユーザー名, パスワードの情報を secret.yml を切り出しておいて, テストで利用する方法の一つとして, Serverspec (厳密には specinfra) の tips で紹介されている property と set_property メソッドを spec_helper 内で利用してみました.

require 'rspec'
require 'rspec-ftp'
require 'specinfra/properties'
require 'yaml'

def property
  Specinfra::Properties.instance.properties
end

def set_property(prop)
  Specinfra::Properties.instance.properties(prop)
end

properties = YAML.load_file('secret.yml')
host = ENV['TARGET_HOST']
ftpuser = ENV['FTP_USER']
set_property properties[host]['users'].first {|u| u['username'] == ftpuser }

Travis CI で docker-compose を使う

元々の rspec-ftp にもテストは書かれていましたが, 今回いくつかのマッチャを追加するにあたり, Docker コンテナで立てた FTP サーバーに対してテストを実行したいと考えました. このような場合, docker-compose を利用するのが良いと考えていますが, 今まで Travis CI で docker-compose を使えることを知りませんでした.

ところが, 確認したところ, ずいぶん前から docker-compose は利用可能な状態になっていたことが判ったので, 以下のような docker-compose.yml を作成してテストを行うようにしました. 特に Travis CI で実行する為に特別な設定は行っておらず, 手元の端末でも docker-compose up -d で一発起動します.

version: "2"
services:
    vsftpd-server:
      image: fikipollo/vsftpd
      container_name: vsftpd-server
      environment:
        - FTP_USER=ftpuser
        - FTP_PASS=supersecret
        - ONLY_UPLOAD=NO
        - PASV_ENABLE=YES
        - PASV_ADDRESS=172.26.0.6
        - PASV_MIN=21200
        - PASV_MAX=21210
      ports:
        - "21:21"
        - "21200-21210:21200-21210"
      networks:
        my_sample_net:
          ipv4_address: 172.26.0.6
... 略 ...
    rspec-ruby23:
      image: ruby:2.3
      build: ./spec/docker/rspec
      container_name: rspec-ruby23
      volumes:
        - .:/work
      command: tail -f /dev/null
      networks:
        my_sample_net:
          ipv4_address: 172.26.0.3
networks:
  my_sample_net:
    driver: bridge
    ipam:
     driver: default
     config:
       - subnet: 172.26.0.0/16
         gateway: 172.26.0.1

networks を利用しているのは, vsftpd で PASV_ADDRESS の IP アドレスをコンテナの IP アドレスに固定する必要があった為です.

引き続き, Travis CI で docker-compose を使いつつ, 複数のコマンドを並列して実行する

複数の Ruby バージョンを使って並列してテストを走らせる為には, matrix を利用して, 環境変数 TEST_TARGET に対象の Ruby バージョンコンテナ名を入れてあげれば良いようです. 以下は実際に利用している .travis.yml です.

sudo: required
matrix:
  include:
  - name: "Ruby 2.5"
    env: TEST_TARGET=rspec-ruby25
  - name: "Ruby 2.4"
    env: TEST_TARGET=rspec-ruby24
  - name: "Ruby 2.3"
    env: TEST_TARGET=rspec-ruby23
services:
  - docker
before_install:
  - docker-compose build ${TEST_TARGET}
  - docker-compose up -d
install:
before_script:
script:
  - docker-compose exec ${TEST_TARGET} rspec
after_script:
notifications:

これを利用することで, 下図のように複数の Ruby バージョンにおいて並列でテストが走ることを確認しました.

f:id:inokara:20181006161310p:plain

以上

FTP という由緒正しいいにしへの技術が今後も使われ続けるのであれば, rspec-ftp を通して FTP について理解を深めていく必要があると感じました. また, 今回のようにインフラの振る舞いをテストするツールとしては infrataster と連携 (もしくはプラグイン化) 出来ないか考えてみたいと思います.

2018 年 10 月 05 日 (金)

ジョギング

  • 山王公園往復
  • 懸垂 4 回
  • 引き続き, 右足に違和感, これはストレッチとかしながら付き合っていくしかないのかな

日課

  • (腕立て x 50 + 腹筋 x 50) x 3

今日も鍋

  • 手羽中の鍋
  • 手羽中は事前にごま油で炒めてから煮込む, 人参等の根野菜も一緒に煮込むとさらに美味しくなる
  • 久しぶりの柚子胡椒も良かった

今日のトゥウィート

12 回目もやります.

明日は

台風が直撃しそう.

2018 年 10 月 04 日 (木)

ジョギング

  • 山王公園往復
  • 懸垂 3 回, 4 回目にいく勇気がほしい
  • 引き続き, 右足に違和感

日課

  • お休み

JAWS-UG 福岡 もくもく会 11 回目

  • 今回はさくらインターネットさんの福岡オフィスで
  • JAWS-UG 会津の方も緊急参戦して頂いた
  • AWS Loft の話題で特に盛り上った
  • もくもくは rspec-ftp を弄って, 自分が欲しかった matcher を追加してプルリクエストを送った (JAWS-UG でやるネタじゃないかもしれないけど...)

とんとん

  • もくもく会の帰りにとんとんで一杯
  • 常連 5 級くらいにはなってきたもしれない
  • 地鶏のタタキとタコの唐揚げ, このくらいで十分お腹いっぱいになる

今日のトゥウィート

「もじゃべ」じゃないらしい.

夢が広がる. ただ, Ruby の複数のバージョンを使ったテストとかしたい場合どうするんだろう.

そう言えば, もくもく会でも似たような話が出た. コンテナを使ったことが無いという層に対して k8s のハンズオンをしても響かない. ちゃんとコンテナを理解すべきだという話をされていて, ホンコレ, なぜ k8s だけが一人歩きしているんだろうなあと改めて考えてしまった. ま, 自分はコンテナもちゃんと理解出来ていないし, k8s なんて全く解ってないのでこれから勉強しなきゃという感じ.

アルゴリズムとプログラミング 「第 1 章 プログラミング」の予習とまとめ

これは

放送大学教養学部の「アルゴリズムとプログラミング」という授業で使われる「アルゴリズムとプログラミング」という教材書籍を自分なりにまとめたものです.

第 1 章では, プログラミングとはなんぞや的な簡単な座学から, プログラミングについて C 言語の初歩的なデータの入出力, 変数や関数の取扱について触れられています.

尚, 本まとめについては, 以下の Github リポジトリで管理しており, 加筆修正はリポジトリのみ行います.

github.com

1. アルゴリズムプログラミング言語

抜粋

  • プログラム (program) とは, コンピュータに対する処理を記述したもの
  • プログラムによって問題を解く為の処理手順はアルゴリズム (algorithm) と呼ばれる
  • コンパイラ (compiler) は, プログラムをコンピュータが実行可能な機械語に変換するソフトウェア
  • インタプリタ (interpreter) は, ソースコードを逐次変換しながら実行しているくソフトウェア

メモ

2. コンパイラ

抜粋

  • コンパイラ (compiler) は, プログラムをコンピュータが実行可能な機械語に変換するソフトウェア
  • ソースプログラムは最終的に実行形式であるターゲットプログラムに変換される

以下はターゲットプログラムへの変換過程.

ソースプログラム (source program)
↓
文法解析器 (syntax analyzer)
↓
意味解析器 (semantic analyzer)
↓
中間コード生成器 (intermediate code generator)
↓
最適化コード生成器 (code optimizer)
↓
コード生成器 (code generator)
↓
ターゲットプログラム (target program)

メモ

  • コンパイルの過程で実は色んな仕事をしているんだな...
  • gcc = GNU Compiler Collection

MacOS X に導入されていいる gcc は以下のようなもの.

$ sw_vers
ProductName:    Mac OS X
ProductVersion: 10.13.6
BuildVersion:   17G65
$ gcc -v
Configured with: --prefix=/Library/Developer/CommandLineTools/usr --with-gxx-include-dir=/usr/include/c++/4.2.1
Apple LLVM version 9.1.0 (clang-902.0.39.2)
Target: x86_64-apple-darwin17.7.0
Thread model: posix
InstalledDir: /Library/Developer/CommandLineTools/usr/bin

尚, 以後は上記の gcc を利用してコードをコンパイルしていく.

3. 入出力と演算

3.1 出力と入力

  • ヘッダファイル stdio.h には printf 関数をはじめ, 様々な入出力に関連したライブラリ関数が含まれている
  • C 言語では必ず 1 つの main 関数を持つ

以下のコードは標準出力に文字データを出力するプログラム.

/* code: ex1-1.c */
#include <stdio.h>

int main()
{
  printf ("The Open University of Japan\n");
  
  return 0;
}

コンパイルして実行してみる.

$ gcc ex1-1.c -o ex1-1
$ ./ex1-1; echo $?
The Open University of Japan
0

main 関数は整数値を返し, プログラムを呼び出したプロセスに値 0 を返す.

以下のコードは scanf 関数を利用した入力の例, 整数型のデータ入力を要求し, 入力されたデータを表示する.

/* code: ex1-2.c */
#include <stdio.h>

int main()
{
  int a;
  printf ("Enter an integer: ");
  scanf ("%d", &a);
  printf ("The integer you entered was %d.\n", a);
  
  return 0;
}

コンパイルして実行してみる.

$ gcc ex1-2.c -o ex1-2
$ ./ex1-2
Enter an integer: 1000
The integer you entered was 1000.

scanf 関数はキーボード等の標準入力からのデータ入力を行うが, リダイレクションによるデータ入力も可能である.

$ echo 100 | ./ex1-2
Enter an integer: The integer you entered was 100.

$ echo 200 > ex1-2.txt
$ ./ex1-2 < ex1-2.txt
Enter an integer: The integer you entered was 200.

3.2 変数

  • C 言語では変数を使用する前に, 変数の宣言を行う必要がある
  • 変数の宣言では, 使用する変数の型を指定する
  • データ型によって値を保存する為に使われるメモリサイズは異なる, また, OS によってもメモリサイズは異なる

以下, データ型の一覧.

データ型 キーワード キーワードの英語
文字列 char character
整数 int integer
浮動小数点数 float floting-point
倍精度浮動小数点数 double double precision floating-point
値無し void void

以下のコードは sizeof 演算子を使ってデータ型のメモリサイズを表示するプログラムである.

/* code: ex1-3.c */
#include <stdio.h>

int main()
{
  char a;
  short b;
  int c;
  long d;
  float e;
  double f;
  
  printf ("char:   %zd byte (s)\n", sizeof (a));
  printf ("short:  %zd byte (s)\n", sizeof (b));
  printf ("int:    %zd byte (s)\n", sizeof (c));
  printf ("long:   %zd byte (s)\n", sizeof (d));
  printf ("float:  %zd byte (s)\n", sizeof (e));
  printf ("double: %zd byte (s)\n", sizeof (f));
  
  return 0;
}

コンパイルして実行してみる.

$ gcc ex1-3.c -o ex1-3
$ ./ex1-3
char:   1 byte (s)
short:  2 byte (s)
int:    4 byte (s)
long:   8 byte (s)
float:  4 byte (s)
double: 8 byte (s)

3.3 算術演算 (arithmetic operator)

  • 演算子には優先順位があり, 乗算, 除算, 剰余は加算, 減算の演算子よりも優先度が高い
演算 演算子
加算 +
減算 -
乗算 *
除算 /
剰余 %

以下のコードは整数型の変数に対して, 算術演算を行うプログラムである.

/* code: ex1-4.c */
#include <stdio.h>

int main()
{
  int a, b, c;
  a = 10;
  b = 3;
  c = 0;
  printf ("a=%d\n", a);
  printf ("b=%d\n\n", b);
  c = a + b;
  printf ("a + b = %d\n", c);
  c = a - b;
  printf ("a - b = %d\n", c);
  c = a * b;
  printf ("a * b = %d\n", c);
  c = a / b;
  printf ("a / b = %d\n", c);
  c = a % b;
  printf ("a %% b = %d\n", c);
  
  return 0;
}

コンパイルして実行する.

$ gcc ex1-4.c -o ex1-4
$ ./ex1-4
a=10
b=3

a + b = 13
a - b = 7
a * b = 30
a / b = 3
a % b = 1
  • 算術関数は math.h をインクルードして利用する
  • gcc や clang 等のコンパイラコンパイルする場合には -lm オプションを付与する必要がある

以下は math.h 定義されている関数の一例.

計算 double 型 float 型 long double 型
sin の計算 sin sinf sinl
cos の計算 cos cosf cosl
tan の計算 tan tanf tanl
べき乗の計算 pow powf powl
平方根の計算 sqrt sqrtf sqrtl
天井関数の計算 sqrt sqrtf sqrtl

以下, べき乗を求める pow 関数を利用したプログラムの例.

/* code: ex1-5.c */
#include <stdio.h>
#include <math.h>

int main()
{
  double x, y, z;
  x = 30.0;
  y = 3.0;
  z = 0.0;
  printf ("x=%f\n", x);
  printf ("y=%f\n\n", y);
  z = pow (x, y);
  printf ("pow (x, y) = %f\n", z);
  
  return 0;
}

コンパイルして実行してみる.

$ gcc ex1-5.c -o ex1-5
$ ./ex1-5
x=30.000000
y=3.000000

pow (x, y) = 27000.000000

3.4 変数と式

以下, 摂氏から華氏への換算を行うプログラムの例. 変数を利用することで複雑な計算式を解くことが出来る.

/* code: ex1-6.c */
#include <stdio.h>
#include <math.h>

int main()
{
  float celsius, fahrenheit;
  
  celsius = 36.5;
  fahrenheit = (9.0 / 5.0) * celcius + 32.0;
  printf ("%f (Celsius) = %f (Fahrenheit)\n", celsius, fahrenheit);
  
  return 0;
}

コンパイルして実行してみる.

$ gcc ex1-6.c -o ex1-6
$ ./ex1-6
36.500000 (Celsius) = 97.699997 (Fahrenheit)

3.5 プログラム中のコメント

  • /* */
  • // の 1 行コメントも利用可能

以下, サンプルコード.

/* code: ex1-7.c */
#include <stdio.h>

int main()
{
  printf ("The Open University of Japan\n");
  /* Web address
     https://www.ouj.ac.jp/ */
  
  // C++ Style comments
  // C99 allows single-line comments
}

一応, コンパイルして実行してみる.

$ gcc ex1-7.c -o ex1-7
$ ./ex1-7
The Open University of Japan

演習問題

問 1.1

天井関数 (ceil) と床関数 (floor) を利用したプログラムを作成しなさい.

/* code: q1-1.c */
#include <stdio.h>
#include <math.h>

int main ()
{
  double x, y;
  
  x = 3.14159;
  y = 0.0;
  printf ("x = %f\n\n", x);
  y = ceil (x);
  printf ("ceil (x) = %f\n", y);
  y = floor (x);
  printf ("floor (x) = %f\n", y);
  
  return 0;
}

そもそも, 天井, 床関数とは.

床関数(ゆかかんすう、英: floor function)と天井関数(てんじょうかんすう、英: ceiling function)は、実数に対しそれぞれそれ以下の最大あるいはそれ以上の最小の整数を対応付ける関数である。

wikpedia より.

コードをコンパイルして実行する.

$ gcc q1-1.c -o q1-1
$ ./q1-1
x = 3.141590

ceil (x) = 4.000000
floor (x) = 3.000000

問 1.2

平方根を計算するプログラムを作成しなさい. sqrt, sqrtf, sqrtl の異なるデータ型の平方根を求める関数を利用すること.

/* code: q1-2.c */
#include <stdio.h>
#include <math.h>

int main ()
{
  float fx, fz;
  double dx, dz;
  long double lx, lz;
  
  fx = 100.00F;
  fz = sqrtf (fx);
  printf ("fx = %f\n", fx);
  printf ("sqrtf (fx) = %f\n\n", fz);

  dx = 100.00;
  dz = sqrt (dx);
  printf ("dx = %f\n", dx);
  printf ("sqrt (dx) = %f\n\n", dz);

  lx = 100.00L;
  lz = sqrt (lx);
  printf ("lx = %Lf\n", lx);
  printf ("sqrt (lx) = %Lf\n\n", lz);
  
  return 0;
}
  • printf 関数では float 型や double 型を出力する場合, %f を用いる
  • scanf 関数で double 型変数への入力を行う場合には %lf が使われる (処理系によって異なる場合がある)

コードをコンパイルして実行する.

$ gcc q1-2.c -o q1-2
$ ./q1-2
fx = 100.000000
sqrtf (fx) = 10.000000

dx = 100.000000
sqrt (dx) = 10.000000

lx = 100.000000
sqrt (lx) = 10.000000

問 1.3

華氏から摂氏への換算を行うプログラムを作成しなさい.

/* code: q1-3.c */
#include <stdio.h>
#include <math.h>

int main()
{
  float celsius, fahrenheit;
  
  fahrenheit = 36.5;
  celsius = (5.0 / 9.0) * fahrenheit - 32.0;
  printf ("%f (Fahrenheit) = %f (Celsius)\n", fahrenheit, celsius);
  
  return 0;
}

コードをコンパイルして実行する.

$ gcc q1-3.c -o q1-3
$ ./q1-3
36.500000 (Fahrenheit) = -11.722222 (Celsius)

これだけだとつまらないので, ちょっと進化させてみる.

/* code: q1-4.c */
#include <stdio.h>
#include <math.h>

int main()
{
  float celsius, fahrenheit;

  printf ("Enter fahrenheit: ");
  scanf ("%f", &fahrenheit);

  celsius = (5.0 / 9.0) * fahrenheit - 32.0;
  printf ("%f (Fahrenheit) = %f (Celsius)\n", fahrenheit, celsius);
  
  return 0;
}

scanf 関数を利用して, 華氏温度にキーボードから入力した値を代入してみる.

$ gcc q1-4.c -o q1-4
$ ./q1-4
Enter fahrenheit: 100.0
100.000000 (Fahrenheit) = 23.555555 (Celsius)

こんな感じ.

以上

感じたことを少々.

  • C 言語は Go 言語っぽい雰囲気を感じた
  • printf するにも, 変数を利用するにも常に型を意識する必要があるのは新鮮だった
  • コード行末の ; の入力を忘れることが度々...
  • C 言語のテストの書き方ってどうするんだろう

2018 年 10 月 03 日 (水)

ジョギング

  • 山王公園往復
  • 懸垂 3 回, おじいさんっぽい人も懸垂やっていて負けられない思いがあった
  • 引き続き, 右足に違和感
  • 体がとにかく硬い

日課

  • (腕立て x 50 + 腹筋 x 50) x 3

今夜はお好み焼

  • お好み焼き専用の粉はよく出来ているなと思った
  • まあまあ美味しかった

今日のトゥウィート

これ, 試したい.

サニーとは, ダイレックスみたいに激安ではないが, そこそこのものがそこそこ安いお値段で手に入る. 複数の若いカップルがキャッキャしながら楽しそうに買い物しているので, この界隈はそういう層も結構住んでいるんだなと思った. また, 学校もあるし, 幼稚園, 保育園も多いので博多駅からほどほど近いにも関わらずベッドタウンと言っても良いのかな.

2018 年 10 月 02 日 (火)

ジョギング

  • 山王公園往復
  • 懸垂 3 回出来た!! (背筋をうまく使いたい)
  • 引き続き右足

日課

  • (腕立て x 50 + 腹筋 x 50) x 3

今夜も鍋

  • 奥さんはあまり好きではないらしいが...
  • 簡単に多くの食材を取れるという点, 体が温まるという点, 準備や片付けの手間が少ないという点がメリットだと考えているので, 今後もじゃんじゃん鍋っていこうと思う

放送大学二学期が始まった

  • ラジオの放送時間が業務中なので事前に本を読んでおいて, 放送を流しながら仕事をするというスタイル
  • 解らないところは録音や radiko.jp のタイムフリーで聞き直すという感じで
  • アルゴリズムとプログラミング第一回目ということで, プログラミング, アルゴリズムとはという簡単な座学から C 言語の超基礎的なことを話していた

ちなみに, C 言語について調べていたら C 言語にも認定資格試験があることを知った. 資格ビジネスに踊らされてしまうのはちょっと嫌だけど, こちらも勉強してみたいと思う.

C言語プログラミング能力認定試験│資格検定のサーティファイ│あなたのスキルアップを応援します|

今日のトゥウィート

11 時半をすぎると色んなニオイがしてくる.

自分よりも若い人が現役引退を決意した会見を深夜のスポーツニュースで見て.

2018 年 10 月 01 日 (月)

ジョギング

  • 山王公園往復
  • 走りはじめは良いけど, やっぱり終盤になると右足が張りまくっている感じ
  • 懸垂 2 回出来た

日課

  • (腕立て x 50 + 腹筋 x 50) x 3

今季初鍋

  • ちょっと寒くなってきたので, 今季, 初めての鍋を執り行った
  • シンプルに適当な野菜と鶏肉と豚, カイワレと大根の細切りを添えてポン酢で食べる
  • じゃんじゃん鍋していこうと思う

今日のトゥウィート

昨日の surface pro 3 ツイートのくだり. よく考えたら micro SD と書きたかったので勢い余って micro SSD と書いていた自分が恥ずかしい.

2018 年 09 月 30 日 (日)

ジョギング

  • 台風の為, おやすみ
  • 右足が張りまくっている感じ, なんとかせねばな...

日課

  • おやすみ

今日のトゥウィート

引っ越し後の片付けしていたら surface pro3 が出てきたので, Micro SSDUbuntu をインストールしてみたら意外にも元気に動き始めたのが嬉しかった.

冷凍のブリの切り身をいい感じで解凍して臭みを取ってからアヒージョ的なものを作ったら美味しかった.