うどん県出身グーナーの呟き

神奈川県在住のグーナーです。プログラムをやっているので勉強していることや作った便利ツールを公開したいと思います。

【pyenv】【python】Windows10にpyenvをインストールしてPython開発環境を整えてみたよ【Windows】

pyenv-win をWindows10にインストールしてPython開発環境を整えたよ

概要

最近C++やhtmlなどばかりやっていたせいか、windows環境にPythonを入れていないことに先ほど気づいたので入れてみました。

個人的にやっぱりこのあたりの環境構築はLinux, Unix系のほうがやりやすいなという印象。

何ならこの間WSLを入れたので、最初からそっちで開発すれば良かったのではないか説もあります。。。

それはさておき、pyenv-winのREADMEにインストール方法がまとまってはいるのですが、意外と落とし穴もあったのでそこを備忘録がてらまとめたいと思います。

そもそもpyenvってなに?

pyenvとは大量にあるPythonの環境を簡単に切り替えてくれる便利ツールです。

Pythonは現在リリースバージョンで3.9.6まで出ています。

Pythonを初めて使う人は何も考えずに最新のリリースバージョンをインストールすればいいのですが、

長年Pythonを使って開発していると昔のバージョンなら使えた機能が無くなるや形式が変わるなどといった問題が出てくることがあります。

もちろんライブラリやシステム側でそういったバージョンの違いを吸収できるように作るべきなのですが、それも中々に大変な作業です。

そこで、このシステムを使う時はPythonのこのバージョンを、という形でバージョンを切り替える機能が求められているわけです。

そこで活躍するのがpyenvです。

pyenvは複数のpythonバージョンを仮想環境として保持することができ、その環境間を簡単に切り替えることができます。

というわけで、Pythonで開発する上で欠かせないpyenvをインストールしたのでその手順を説明していきます。 f:id:ryo_udon:20210801144019p:plain

(余談)Anaconda

実はpyenvの他にAnacondaと呼ばれる、pythonバージョンを管理できるとても便利なツールがあったのですが、

つい最近商業利用が有償になってしまいました。

今回、個人利用しか考えていませんが、念のためAnacondaは使わずpyenvのみで運用していきます。

pyenv-winインストール手順

pyenvはもともとLinux, Unix(Mac)向けのツールになっていて、そのままではWindowsでは使えません。

ですが、最近ではそれをWindowsでも使えるようにしてくれたpyenv-winGithubで公開されています。

Install方法についてもREADMEに簡潔にまとめられており、かなり簡単にインストールできるようになっています。

開発者の方には頭が上がりません。

1. pyenv-win ダウンロード

まず初めにpyenv-winのパッケージをインストールします。

Githubから取ってくる形になるので、インストールは主に以下の二つの方法があります。

  • zipファイルをダウンロードする
  • git cloneしてくる

まず最初に、最終的にインストールするとどういうディレクトリ構成になるかを紹介します。

C:\Users\<User>\.pyenv
├───pyenv-win
    ├───bin
    ├───libexec
...

zipファイルをダウンロードする場合

  1. リンク先からpyenv-winをダウンロードします。
  2. 『.pyenv』ディレクトリ(フォルダ)がない場合、『C:\Users(自分のユーザ名)\』に.pyenvディレクトリを作成します。
  3. ダウンロードした.zipを解凍し、中に入っているpyenv-winを2.で作った.pyenvの中に移動します

git cloneする場合

git clone する場合は以下のコマンドをたたくだけです。

git clone https://github.com/pyenv-win/pyenv-win.git "$HOME/.pyenv"

2. Pythonアプリインストーラの無効化

ここから先に進む前に自分のwindowsのバージョンを確認してください。

Windows10 1905よりも新しいバージョンの場合、Python App Installerがデフォルトで有効になっています。

その場合、ここから先の作業をするまえに無効化する必要があります。

やり方は簡単です。

下の画像のように左下に『アプリ実行エイリアスの管理』と打ち込んで検索をかけて、実行してください。

f:id:ryo_udon:20210801143737p:plain

すると次のようにアプリの一覧が出てきますので、この中からPython, Python3のアプリインストーラをオフにしてください。

f:id:ryo_udon:20210801143819p:plain

3. pyenv-winの環境変数を設定する

次に.pyenvに移動したpyenv-winを実行できるようにpyenvの環境変数の設定をしていきます。

まず、左下のスタートメニューを右クリックし、Powershellを起動します。

その後、以下のコマンドを実行します。

[System.Environment]::SetEnvironmentVariable('PYENV',$env:USERPROFILE + "\.pyenv\pyenv-win\","User")
[System.Environment]::SetEnvironmentVariable('PYENV_HOME',$env:USERPROFILE + "\.pyenv\pyenv-win\","User")

これでpyenvの環境変数は設定完了です。

4. Pathを設定する

最後にパソコンがpyenvを見つけられるようにPathを設定していきます。

同じくPowershellを開き、以下を実行します。

[System.Environment]::SetEnvironmentVariable('path', $env:USERPROFILE + "\.pyenv\pyenv-win\bin;" + $env:USERPROFILE + "\.pyenv\pyenv-win\shims;" + [System.Environment]::GetEnvironmentVariable('path', "User"),"User")

pyenvが使えるかを確かめる

ここまで出来たら動くかを確認していきます。

ターミナルやpowershellを起動してもらい、以下を入力してください。

pyenv --version

これでダウンロードしたpyenvと同じバージョンが表示されればインストールの完了です。

また

pyenv

と打つとpyenvのコマンド一覧を確認できます。 f:id:ryo_udon:20210801143943p:plain

pyenvの使い方

pyenvに様々なversionのPythonをインストールする (pyenv install)

このコマンドは様々なPythonのバージョンをインストールするコマンドです。

どのPythonがインストールできるかは

pyenv install -l

で確認ができます。

これを実行すると以下のようにインストール可能なバージョンが大量にリストになって表示されます。

f:id:ryo_udon:20210801144151p:plain

この中からインストールしたいバージョンを選び次のコマンドを実行するとインストールが完了します。

pyenv install 3.9.6

インストール済みのPythonバージョン一覧を確認する( pyenv versions)

今、どのバージョンがインストール済みかを確認したい場合、こちらのコマンドを実行すると、

インストール済みのバージョンの一覧が表示されます。

また、pyenvの仮想環境が実行済の(現在すでに動いている)場合、動いているバージョンの頭に*がついて、どれが実行されているかを確認できます。

f:id:ryo_udon:20210801144206p:plain

インストールした環境を実行する( pyenv exec )

pyenvの仮想環境を立ち上げる場合は以下のコマンドを実行します。

pyenv exec [バージョン]

なお、実行前にそのバージョンがインストールされている必要があります。

指定した環境を自動的に実行するようにする( pyenv global)

例えばバージョン3.9.6を常に自動的に実行されるようにしたい場合は以下のようにコマンドを設定します。

pyenv global 3.9.6

(余談)Vscodeの設定

VscodePythonの開発をする際Interpreterを設定しろと要求されますが、pyenvの場合簡単です。

Interpretorを設定せず、ただVscodeそのものを再起動します。

すると先ほど設定した環境変数をもとに勝手にVscodeが見つけてくれます。便利ですね。

注意: 再起動の際は必ずVscodeの全ウィンドウが閉じられているのを確認してください。Vscodeがパスや環境変数を読み込むのはVscodeが起動したときのみのようですので、pyenvのインストール時に設定した環境変数などが反映されません。

終わりに

というわけで、今回はwindowsPython環境を構築する方法について説明しました。

最近やっとwebページの構造が理解できてきたので、こんどはそれをDjangoなどのライブラリを使ってpythonベースのwebページなどを作っていきたいと思います。

ではでは。

【WSL2】【VSCode】Windows10にWindows Subsystem for Linux(Ubuntu 20.04)をインストールしてみた & VSCodeで環境を整えた【インストール方法紹介】【Ubuntu20.04】【Windows10】

Windows10にWindows Subsystem for Linux(Ubuntu 20.04)をインストールしてみた & VSCodeで環境を整えてみたよ

概要

C++のモジュールテストの勉強をしようとGoogle先生とお話していたところ、そういえばUbuntu環境を整えていないなと思い、前からやってみたかったWSL2, Ubuntu20.04のインストールとその開発環境を整えたので備忘録として残します。

WSLとは?

そもそもWSLとは何か。マイクロソフト公式サイトを見てみると以下のようにありました。

The Windows Subsystem for Linux lets developers run a GNU/Linux environment -- including most command-line tools, utilities, and applications -- directly on Windows, unmodified, without the overhead of a traditional virtual machine or dualboot setup.

"WSL(Windows Subsytem for Linux)とは、開発者に仮想マシンデュアルブートのインストールを必要とせずに、Windows上で直接GNU/Linux環境(例えばコマンドライン、アプリケーションなど)を実行できるツールです。"

かつて、プログラマ=Macという時代がありました。

(OSの歴史は簡単にまとまっているこちらのサイトが参考になると思います)

その最大の要因は、MacUnix系のOSのため、Linuxチックにソフトを開発できるということがメリットとしてありました。 (コマンドもLinuxと同じものが多い)

対してWindowsIBMと共同開発していたMS-DOSの系譜なので、コマンドラインなどが上のMac, Linuxとは違っていました。

世間的にはWindowsのほうがシェアが大きいですが(90%以上)、意外と組み込み系はLinuxベースの物も多く、特に研究用途などではUnixベースで書かれることが多くありました。

あと、個人的な意見ですが、コマンドラインで環境を構築しずらかったのもWindowsが避けられていた原因の1つだと思います。

というような背景もあり、しばらくプログラマ=Mac (or Linux)という状態が続いていたのですが、数年前(2016年)ついにWindowsが動きました。

そう、WSL(Windows Subsystem for Linux)のリリースです。

これによって、WindowsでもLinux, Unixベースのソフト開発ができるようになったためよりWindowsが優位になったのです。

(個人的にはいまだにWindowsはイライラする点が多いですが。。。)

WSLは何ができるの?

じゃあ、WSLは何ができるのか。

基本的にWSLはコマンドラインで動かす形になりますが、Ubuntuなどを入れることで簡単なLinuxのコマンドからGUIアプリケーションまで幅広く実行できます。参考

自分みたいにUbuntuのためにRaspberry PiUSBメモリを用意するといった作業が必要なくなります。

どうやってWSLをインストールするの?

基本的には公式サイトに沿ってコマンドを打ち込んでいくだけです。

Windowsのバージョン確認

まず、Windowsのバージョンを確認します。

以前はWindows Insider Programに参加する必要があったはずですが、なぜか自分は参加せずとも動かすことができました。

(もしかしたら手動インストールをする分にはInsider Programへの参加は必要がないのかもしれません)

ちなみに自分は以下のバージョンで実施しました。

エディション  Windows 10 Pro
バージョン 21H1
OS ビルド    19043.1110

上よりも新しいバージョンになっていれば問題ないと思います。

公式には以下のような記述があります。

WSL 2 に更新するには、Windows 10 を実行している必要があります。

x64 システムの場合:バージョン 1903 以降、ビルド 18362 以上。

ARM64 システムの場合:バージョン 2004 以降、ビルド 19041 以上。

18362 より前のビルドは WSL 2 をサポートしていません。 Windows 更新アシスタントを使用して、お使いのバージョンの Windows を更新します。

もし更新が必要な場合は以下の通りです。

更新方法

1 まず左下のWindowsマークから設定を開きます。

f:id:ryo_udon:20210723123340p:plain

2 設定の中から『更新とセキュリティ』を選択します。

f:id:ryo_udon:20210723123355p:plain

3 その後、赤丸で囲った更新プログラムのチェックを押してください。

f:id:ryo_udon:20210723123408p:plain

4 もしアップデートがあれば自動でアップデートしてくれます。

ただし、再起動が必要なアップデートがあれば可否が聞かれますので、再起動してもいいように他のアプリは落としておきましょう。

WSLのインストール

WSLには二つバージョンがあります。WSLとWSL2です。

違いはこちらの公式サイトに載っています。

基本的にはWSL2の方がパフォーマンスが上がること、制限なしのLinuxカーネルを利用できることがあげられてます。

唯一WSL2ができない(非推奨な)ことが、Windows <-> Linux間でファイルを同時に操作することですが、正直そのようなソフトを作ることはかなりレアケースだと思いますので、基本的には何も考えずにWSL2をインストールすればいいと思います。

(新しい物好きの自分は何も考えずにWSL2をインストールしました)

Powershellを管理者権限で開く

これから3つのコマンド(オプション機能を2個有効、WSLのバージョン変更)をPowershellで実行しますが、管理者権限が必要になります。

もし開き方が分からない方は以下の通りに実行してください。

  1. まず左下のWindowsマークを右クリックします

  2. Windows Powershell (管理者)から管理者権限を付与したPowerShellを実行できます

f:id:ryo_udon:20210723123518p:plain

LinuxWindowsサブシステムを有効にする

まずLinuxWindowsサブシステムというオプション機能を有効にする必要があります。

先ほど開いたPowerShellで以下のコマンドを実行してください。

dism.exe /online /enable-feature /featurename:Microsoft-Windows-Subsystem-Linux /all /norestart

注意:ちなみにWSLをインストールする方はここで再起動をして"Linuxディストリビューションをインストールする"に進んでください。

仮想マシンの機能を有効にする

次に二つ目のオプション機能、仮想マシンプラットフォームを有効にします。

先ほどのPowerShellで以下のコマンドを実行してください。

dism.exe /online /enable-feature /featurename:VirtualMachinePlatform /all /norestart

ここで再起動を求められますので「Y」を押してください。 (Yを押した瞬間に再起動しますので、びっくりするかもしれません)

Windowsが再度立ち上がったら次に進んでください。

Linuxカーネル更新プログラムパッケージをダウンロードする

ここではLinuxカーネル更新プログラムパッケージをインストールしていきます。

公式サイトのこちらのリンクからパッケージをダウンロードし、実行してください。

管理者権限を求めるウィンドウやインストールしますか、などを聞かれるのですべて「はい」を押してください。

WSL2を既定のバージョンとして設定する

再度PowerShellを開き(自分は念のため管理者権限で開きました)、WSLの既定バージョンをWSL2とするために以下のコマンドを実行します。

wsl --set-default-version 2

これで基本的な設定は終わりです。

Linuxディストリビューションをインストールする

さて、やっとLinuxをインストールしていきます。

上のパッケージをインストールすると、Microsoft Storeから様々なLinuxをダウンロードできるようになります。

ここでは例としてUbuntu20.04 LTSを入れていきます

上のリンクに飛んでいただくと入手ボタンがあるので、それをクリックします。

するとインストールするというボタンが上に現れますので、それもまたポチリます。

f:id:ryo_udon:20210723123738p:plain

これでインストールは終わりです。

Linuxディストリビューションのアカウントとパスワードを作成する

スタートメニューなどからLinuxディストリビューションを起動すると、最初の一回のみ以下のような画面がでてきます。

f:id:ryo_udon:20210723123807p:plain (撮り忘れたので公式より引用)

ここでユーザアカウント名とパスワードを設定すれば、WSL2 + Ubuntu20.04のインストールは完了です!!

WSL2 + VSCodeによる開発環境構築

ここからはWSL2とVSCodeを使った開発環境構築の方法を説明していきます。

こちらの方法は公式サイトにも載っています。

構築前に確認すべきこと

確認すべきことは主に以下の二つです。

  • WSLのインストールが完了していること
  • Windows側にVSCodeをインストールしていること

VSCodeがインストールできていない方はこちらのサイトからダウンロードしてインストールしてください。

VSCodeでリモート開発拡張パックをインストールする

VSCodeには様々な拡張機能を追加することができます。

例えば、プログラミング言語ごとのintellisense(補完機能)やデバッグ機能、自動でCMakeListを作ってくれる機能だったり、markdownのプレビューができるなど様々です。

自分がどういう拡張機能を使ってるかはまた今度紹介したいと思います。

さて、リモート開発拡張パックをインストールするのですが、ここで選択肢が二つあります。

公式サイトでは、Remote DevelopmentというWSLに限らず、他のパソコンや仮想マシンSSHでアクセスしてリモート開発できる拡張パックをインストールすることを推奨しています。

ですが、ただWSLでのみ開発することを考えている方にはこのパックはToo Muchな気がします。

また設定もいくつか必要となります。

なのでここでは、Remote WSLを紹介します。

どちらもMicrosoftが提供している公式拡張パックですが、後者はWSLに限定しています。

なのでほぼ何も考える必要もなくWSL上のUbuntuにリモートアクセスすることができます。

インストール方法は、リンク先のインストールを押すか、VSCode拡張機能から検索してインストールすることができます。

Remote WSLの使い方

1 まずVSCodeウィンドウの左下にある緑のボタンをクリックします。

f:id:ryo_udon:20210723123905p:plain

2 すると下のようなコマンド一覧がVScodeウィンドウの真ん中上部に表示されます。

f:id:ryo_udon:20210723123937p:plain

3 この中から『New WSL Window』を選択します。するとUbuntu20.04のディストリビューションに接続した状態のウィンドウが開かれます。

あとはいつものVScodeの感覚でファイル編集や操作ができます。

(余談)Linuxディストリビューションの切り替え方

ちなみに複数のLinuxを入れている場合(例えば18.04と20.04など)、簡単に切り替えができます。

  1. 先ほどと同じく左下の緑色のボタンをクリック

  2. コマンド一覧の中から『New WSL Window using Distro ...』を選択

f:id:ryo_udon:20210723123954p:plain

  1. 使いたいディストリビューションを選択

f:id:ryo_udon:20210723124004p:plain

で開く切り替えることができます。

終わりに

というわけで、今回はWSL2+Ubuntu20.04のインストールからVSCodeの設定までやってみました。

過去に山ほどLinuxインストールで苦しんできた身としてはこれほど簡単にインストールできるのは感動ものでした。

ぜひ皆さんも試してみてください。

ではでは!!

【C++】C++で簡単なマルチスレッドをやってみたよ【マルチスレッド】【サンプル】

C++ マルチスレッド処理を書いてみたよ

github.com

なぜやってみたのか

今回はマルチスレッド処理をC++で書いてみましたのでその紹介になります。

なぜマルチスレッドをしてみようとおもったのか。

それは最近センサの受け取って処理をまわすという部分を書いていて、まじめにマルチスレッドをやる必要があるなと感じたためです。

例えば10msごと(100Hz)にデータが送られてくるものを受け取って、50ms(20Hz)で処理を回したいとなったとき、どういう風に実装するべきでしょうか。

色々と方法はあると思います。

例えば、50msで回したい処理が実際は実行に20ms程度しかかからない場合、

それに加えて受信処理が短い時間で完了するのであれば、割り込み処理で十分かもしれません。

ですが、もし50msで回したい処理が40ms程度かかるものだった場合どういう方法があるでしょうか。

そう考えた時に思いつくのが、処理を複数に分ける(マルチスレッド)ということです。

処理を複数に分ける(マルチスレッド)、とは?

そもそも「スレッド」、「CPU」とは?

そもそも皆さんはパソコンの上で実行されるプログラムはどのようにして動いているかご存じでしょうか。

素子がどのように動くのかという細かい部分はおいておいて、基本的にC++などで書かれたプログラムはCPUで計算、実行されます。

ここでCPUの動きに注目します。

皆さん、パソコンを選ぶ時にこんな言葉を聞いたことはないですか?

  • クロック数
  • コア数
  • スレッド数

これが何か分かりやすく説明してくれているサイトがあるので先に紹介しておきます。

簡単に説明すると、下のようになります。

クロック数 = 頭の回転の速さ (例: 2.0GHz よりも 5.0GHzのCPUの方が計算能力が高い)

コア数 = 何個頭脳を持っているか (例: シングルコア: 1つ、 デュアルコア: 2つ、 クアッドコア: 4つ)

スレッド数 = 各頭脳(コア)が余裕がある時に何個並列で処理ができるか (4コア8スレッドという書き方だと、CPUで余裕があれば2個並列で処理して効率化できる)

すでにお気づきの方もいるかもしれませんが、

CPUの性能というのは一つの項目で決まるのではなく、基本的に上の三つの性能のバランスによって決まってきます。

ではどういう時にどの項目を重視すればいいのでしょうか。

クロック数最重視する場合

ここで処理の話も入ってきますが、例えばそのパソコンでは常にプログラムが1つだけ動くとしましょう。

こうなるとCPUに求められるのは、何よりそのプログラムを早く、安定して回すことになります。

こうなると他に処理をする必要がそもそもないので、コア数やスレッド数は小さくても問題がなくなります。

なので、マイコンなどと呼ばれるような組み込み系のコンピュータはコア数が少なく、シングルコア、デュアルコアが多いです。

(ここではマイコンを例に出しましたが、世の中にシングル、デュアルでそれだけクロック数が高いCPUが出回っていないので、例としては少しずれています)

コア数(あるいはスレッド数)を最重視する場合

例えば処理の重い(各コアの使用率が常に90%近くになるような)プログラムがパソコンの中で3個動くとしましょう。

この時点で察しの通り、上のようなクロック最重視、コア数が少ないものでは3つもの重い処理を回すことができません。

なので、ここではコア数が重要になります。

コア数が最低、その処理を回しきれるだけの数とクロック数を持っているかという確認が必要になるわけです。

(余談) じゃあパソコンを選ぶ時はどういうCPUを選べばいいの?

基本的に用途によりますが、日常的な使い方(ネットサーフィン、メールチェックなど)であれば、正直それほど悩む必要はないでしょう。

予算にもよりますが、Intelなら最低i3 ~ i5、Ryzenなら3以上を選んでおけばそうそう外れることはないでしょう。

(予算があるなら4コア8スレッドで2.0GHz以上のクロック数のものを買うととりあえず後悔はしないと思います。)

(さらに余談) でもパソコンはこれだけでは決まらない

上ではあまり言及しませんでしたが、家庭用パソコンだとCPU以外にもメモリ(一時的に処理しているデータを保存する場所、荷物の仮置き場)、ストレージ(データの倉庫)の二つにも注目する必要がありますので、なかなか決めるのは難しいかもしれません。

なので購入する際には周りの詳しい人に相談しましょう。

ちなみにお店の店員には相談しない方がいいかと思います。お得ですよといわれながら、結構な型落ちのパソコンなどを押し付けられることがあるらしいです。

マルチスレッドとは?

やっと本題です。

マルチスレッドとは、上のCPUの説明である程度察している方もいるかと思いますが、1つのプログラムで複数のスレッド(コア)を使って処理できるようにすることになります。

何がよくなるのかというと、簡単にいうと今まで2.0GHzのコア1つでしか処理ができなかった物が、2.0GHzのコア2つ(あるいはそれ以上)で処理ができるようになります。

なので理論上、プログラムをより高速で(リアルタイムで)実行することが可能になります。

ただし、ここで注意すべきなのは、あくまで理論上可能になるだけで、逆に速度が落ちる可能性もあります。

こういう話でよく聞くのは、最近はやりのGPUプログラミングです。

GPUというのはゲームなどで人や車が3Dで描画されているかと思いますが、その描画をより高速で行うことに特化した演算装置です。

CPUとの一番の違いはクロック数が低いコア数が圧倒的に多い(100倍から1000倍、最近のモデルではそろそろ10000倍になりそう)ことです。

なので、最近のAIやディープラーニングと呼ばれる膨大な並列計算を必要とする技術ではCPUではなくGPUを使って実装しています。

ここまで聞くと速くなる要素しかないですが、落とし穴があります。

それはどのように複数のスレッドに分散するかを決めるところです。

じつは意外と分散して処理をする直前の割り振りを決める処理が重くなって、全体の動作が重くなるということが多々あります。

(最近は優秀なライブラリなどのおかげで減ってきているようですが。。。)

なので、ここで言いたいのは、マルチスレッドをやる前に、「本当にマルチスレッドは必要なのか」「速くなりそうな処理なのか」を判断する必要があります。

分からないから取り合えずも全然ありだと思います。

なので処理時間で悩まれてマルチスレッドをと考えているそこのあなた、とりあえずまずはシングルスレッドのままで処理の効率化を頑張りましょう!!

本命のソースコード紹介

github.com

ここでは上のレポジトリ内にあるudp_pub_test.cpp, udp_sub_test.cppを紹介していきます。

この二つはローカルでUDP通信を行い、pub_testからデータを送信します。

UDP通信についてはまた別の記事で紹介するので、ここでは二つのプログラムの間をUDPっていう通信方式でデータを渡している、とだけイメージしてください。

sub_test側は二つスレッドが立っており、1つは常にデータの受信を待ち続け一定数データが溜まるとデータを読んでいいよフラグを立てます。 もう1つのスレッドは毎ループ、そのフラグを確認し、データが溜まっていればそれを読みに行って、画面に表示するという簡単なプログラムです。

ここでポイントとなるのは以下の二つになります。

  • スレッドの立て方
  • mutexのかけ方

ソースコードを追いながら順番に説明していきます。

スレッドの立て方

上の概要で説明したように、sub_test側では二つのスレッドが立っています。

  • データ受信
  • データ解釈

スレッドを立てているのは以下の部分です。 基本的にスレッドを立てる上で使用しているライブラリはC++11にデフォルトで入っている下の二つのライブラリです。

#include <thread>
#include <mutex>
int main(void)
{
    struct sockaddr_in addr;

    addr.sin_family = AF_INET;
    addr.sin_port = htons(12345);
    addr.sin_addr.s_addr = INADDR_ANY;


    std::thread th1(ReceivePacket,addr);
    std::thread th2(PointPurseMain, 20);
    th1.join();
    th2.join();

    return(0);

}

std::threadで宣言しているのが各スレッドになります。

ここで別スレッドで実行する関数を指定したあと、その下のjoin()関数を実行することで、別スレッドで処理が実行されます。

ちなみに上ではth1でデータ受信の関数名とその引数であるsockaddr_in(UDP通信用)を、th2でデータ解釈をする関数名と引数である関数を回すHz数を与えています。

簡単ですね。

mutexとは?

ここからがマルチスレッドで難しいところです。

マルチスレッドの処理を書いた時に一番陥りがちなのが、メモリのアクセス違反です。(実行時にSegmentation Faultなどとでるエラーです)

これはなぜ起きるのか。

パソコンにはデータを一時保存するメモリと呼ばれる装置が搭載されています。

基本的にプログラムを実行した時の変数だったりはこのメモリに一時保存されます。

メモリの中は賃貸の集合住宅のように区分けされていて、ここはしばらくの間、この変数さんの部屋よ、という形で確保されていきます。

ここで上のアクセス違反という言葉と併せて予想がついた方もいるかと思いますが、この各部屋には一度に一人の人しか出入り(書き込み)できません。

f:id:ryo_udon:20210703131940p:plain

中がショールームのように固定されていて、外から覗くだけであれば人数制限はありません。(いやな世界ですね。。。)

ただし、誰かが部屋の中に入ると、すべての窓はカーテンが閉められ、誰も見てはいけない状態になってしまいます。

あとは簡単ですね。そんな状況で家の中に入ろうとしたら通報されます。(不法侵入or覗きですね)

その通報された結果がSegmentation Faultです。

f:id:ryo_udon:20210703131839p:plain

じゃあ、これを防ぐためにどうすればいいのか、それをするのに必要なのが、

  • アクセスしていい変数(部屋)を別に用意する
  • アクセスする前に誰もいないかの確認をする

の二つになります。

ちなみに後者がここでメインで説明するmutexのことになります。

アクセスしていい変数を別に用意する

関数を見ながら説明していきます。

まず1つ目の説明をするために、データ受信部分を見ていきます。

void ReceivePacket(struct sockaddr_in addr)
{
    int sock = socket(AF_INET, SOCK_DGRAM, 0);
    bind(sock, (struct sockaddr *)&addr, sizeof(addr));

    uint8_t tmp_buf[BUFFER_SIZE];
    uint8_t* p = tmp_buf;

    int point_num = 0;
    int point_limit = POINT_BUFFER_LIMIT;
    int point_size = SINGLE_POINT_DATA_SIZE;
    int point_id = 1;

    while(1)
    {
        recv( sock, tmp_buf, sizeof(tmp_buf), 0);
        memcpy( p, tmp_buf, point_size);
        p += point_size;
        point_num++;

        if(point_num > point_limit)
        {
            printf("Receive Func:PointLimit\n");
            std::lock_guard<std::mutex> lock(mtx_);
            memset(buf_, 0 , sizeof(buf_));
            memcpy(buf_, tmp_buf, point_num * point_size);
            memset(tmp_buf, 0, sizeof(tmp_buf));
            p = tmp_buf;
            point_num = 0;
            data_set_flag_ = true;
        }

    }
    close(sock);

}

内容はいたってシンプルです。

毎ループ、データが来るのを待って、データが来たらそれをBuffer(データを格納するスペース)にポコポコ入れてあげるだけです。

そのあとに一定数データが溜まったら上で書いたフラグを立てた上で、アクセスするようの別のBufferにコピーします。

そうここでコピーしている先の別のBufferが上にあげた1つ目になります。

ただ、こちらについては必須ではありません。

先ほど話したように、各メモリの書き換えができるのは一度に1つのみです。

なので、他のスレッドがアクセスしている間、そのメモリにアクセスしないのであれば、こちらのように別にBufferを用意する必要はありません。

アクセスする前に誰がいるかを確認する(mutex)

イメージは各メモリ(部屋)に入る扉に、在室、退室の看板が立っているイメージです。

入る時は在室にして、出る時は退室にしましょう。入る前には看板を確認して、在室になっていたら待ちましょう。

mutexはただそれを簡単に行ってくれるだけです。

では簡単にソースコードを見ながら簡単に説明していきます。次に見ていくのは解釈部分です。

void PointPurseMain( int hz )
{
    std::chrono::system_clock::time_point start, now;
    start = std::chrono::system_clock::now();
    now = std::chrono::system_clock::now();
    double now_process_time = std::chrono::duration_cast<std::chrono::milliseconds>(now-start).count();// to milliseconds

    double process_target_time = 1000 / (double)hz;
    int process_time = 0;

    int point_num = POINT_BUFFER_LIMIT;
    int point_size = SINGLE_POINT_DATA_SIZE;
    int point_id;

    while(1)
    {

        std::lock_guard<std::mutex> lock(mtx_);
        if(data_set_flag_)
        {

            for(int i=0; i<point_num; i++)
            {
                point_id = i;
                printf("Main Thread:%d of points are set!! %d th point is (%d, %d, %d, %d)\n", 
                        point_num,
                        point_id,
                        buf_[0 + point_size * point_id], 
                        buf_[1 + point_size * point_id], 
                        buf_[2 + point_size * point_id], 
                        buf_[3 + point_size * point_id]);
            }
            data_set_flag_ = false;
        }

        now = std::chrono::system_clock::now();
        now_process_time = std::chrono::duration_cast<std::chrono::milliseconds>(now-start).count();
        process_time = (int)(process_target_time - now_process_time);

        if(process_time > 0)
        {
            std::this_thread::sleep_for(std::chrono::milliseconds(process_time));
            start = std::chrono::system_clock::now();
        }
    }
}

ここで重要なのはこれです。

        std::lock_guard<std::mutex> lock(mtx_);

上の受信関数でも呼ばれていましたが、この一行を呼ぶことでメモリの入口前の看板を確認し、退室になっていれば在室にした上でアクセスを始めます。

引数で与えている"mtx_"はグローバル変数で頭で宣言しています。

なので、これらのポイントを抑えることで簡単にマルチスレッド処理を書くことができます。

ちなみに二つターミナルを開いて、片側でudp_sub_test, もう一方でudp_pub_testを実行すると以下のように画面に出力されます。

f:id:ryo_udon:20210703132325p:plain

これでちゃんと動いていることがわかりますね。

まとめ

というわけでマルチスレッド処理をC++で書いてみました。

組み込み系をやっている人間として、処理速度に困ったとき最後の砦として存在するマルチスレッドを勉強してみましたが、意外と簡単に書けるなという印象です。

是非とも皆さんも試してみてください!!

ではでは。

【Rust】【チュートリアル】世界のトヨタが使っているというRustを使ってみたよ、その2

世界のトヨタが使っているというRustを使ってみたよ、その2

概要

最近、自動運転界隈で注目されているトヨタが開発中のCarOS(Arene)がRustという言語で開発されているということを聞きつけ、

ミーハーな自分を抑えられなかったので簡単に触ってみました。

かなりしっかりしたチュートリアルがあるので、現在はこれを勧めている段階のメモ書きになります。

今回は少しRustの特徴などについても触れていきます。

ターミナルからの入力を受け取る

このページから始めていきます。

このチュートリアルでは、いままでターミナル上に出力するだけでしたが、そうではなく、外部からの入力(数字、文字)の受け取りができるソフトを作っていきます。

ただ、このページの本質はそこではなく、Rustという言語を扱う上で重要な以下の点の説明がなされています。

  • 変数の宣言方法
  • immutable(後で変更できない)とmutable(後で値を変更できる)な変数の違いと宣言方法
  • ライブラリのインポート(インクルード)方法
  • Exception処理の重要性

まず、前回と同じくcargoを作っていきます。

Rustのworkspaceに移動し、以下のコマンドを実行します。

cargo new guessing_game

guessing_gameはプロジェクト名になるので、好きな名前で大丈夫です。

次に guessing_game/src/main.rs を好きなエディタで開いていただき、以下をコピペしてきます。

// std ライブラリ内のio(ファイル入出力)ライブラリをインポート
use std::io;

fn main() {

    // 文字列を出力するマクロ
    println!("Guess the number!");

    println!("Please input your guess.");

    // Point!!
    // letは変数を宣言するよというマクロ
    // mutはこの変数をmutable(後で変更可能)な変数として宣言するよというマクロ
    // Rustの変数はデフォルトはimmutable(後で変更できない, Cのconstかと思ってたが、違うらしい)

    // let foo = 5; // immutable
    // let mut foo = 5; //mutable


    // String::new() String型(文字列型)の空のインスタンス(値を入れる箱)を作成
    let mut guess = String::new();

    // io::stdin()
    //    .readline( &mut guess)
    //ターミナルからのinput を受け付ける関数
    // 引数はmutableな変数へのreference(&) <- C++の参照渡し

    // Point!!
    // .expect("Failed to read line");
    // Rustは関数からの返り値にたいして、

    io::stdin()
        .read_line(&mut guess)
        .expect("Failed to read line");

        println!("You guessed:{}", guess);

}

インポート

コメントアウト部分は自分のメモ書きになりますので、消して頂いて大丈夫です。

まず、2行目から

use std::io;

これはCでは

#include stdio.h

Pythonでいうimportに相当するものです。

ここではi/oに関わるライブラリのみが使われていますが、他にもstdライブラリだけでもかなりの数が提供されています。

なお、RustはPythonのようにインポートなしでもいくつかの機能が自動的にインポートされていますが、 Rustという言語の性質上、使わないライブラリをなるべく減らしたいため、こちらのページ の機能のみとなっています。

ちなみにこの自動的にインポートされる機能の総称としてpreludeと名付けれらています。

main文

次に4行目、

fn main(){

次に各プログラムのEntry Point(開始地点)になるmain関数です。 ここは他の言語と同じく、変数名を変えてはいけません。

Pythonでのdefと同じく、fnがRustの中で関数を宣言するマクロになります。

変数の宣言

今回のチュートリアルで恐らく一番重要なのがこの部分になります。

上のコードでの11行目。

    let mut guess = String::new();

これがString型の変数の宣言になります。

まず、マクロを羅列して機能を一覧にしてみます。

  1. let -> 変数を宣言しますよというマクロ

  2. mut -> mutable(変数の中身を後で変えられるよう)にしますよというマクロ

  3. String::new() -> String型のインスタンス(値を入れる箱)を作りますよというマクロ

ここで重要なのは2.のmut

つまり、Rustでは基本的に変数はimmuttableな = "後で値が変更できない” ものになります。

これは高信頼性を謳う言語ならではという感じですね。

なので、例えば以下のようなコードがあったとします。

fn main() {
    println!("Guess the number!");
    println!("Please input your guess.");

    let mut guess = String::new();

    let test = guess;
    println!("Test={}",test);
}

このコードの場合、testという変数は宣言時にguessという変数の値をコピーして格納します。

なので、実行するとguess(何も値を入れていないので空)になります。

ワーニングが出ますが、これは値を変更していないのにguessをmutableにしているためなのでここでは無視します。

ryo_udon@ryo-udon-desktop:~/workspace/rust_workspace/guessing_game$ cargo run
   Compiling guessing_game v0.1.0 (/home/ryo_udon/workspace/rust_workspace/guessing_game)

warning: variable does not need to be mutable
  --> src/main.rs:23:9
   |
23 |     let mut guess = String::new();
   |         ----^^^^^
   |         |
   |         help: remove this `mut`
   |
   = note: `#[warn(unused_mut)]` on by default

warning: 1 warnings emitted

    Finished dev [unoptimized + debuginfo] target(s) in 1.14s
     Running `target/debug/guessing_game`
Guess the number!
Please input your guess.
Test=

しかし、これに対して以下のようにコードを変えて実行するとこうなります。

fn main() {
    println!("Guess the number!");
    println!("Please input your guess.");

    let mut guess = String::new();

    let test = guess;
    test = "something".to_string(); //<-この行を追加
    println!("Test={}",test);
}

実行結果:

f:id:ryo_udon:20210131183126p:plain

エラーを簡単に説明すると、最初にtestという変数を宣言したときにもう変更しないよ(immutable)と宣言したのに値を変更しているよ、と怒られています。

C言語などでは、基本的にconstを使わない限り、このようなエラーはでません。

そう考えるとこれもRustの特徴のように感じます。

ちなみにRustにもconstがあります。

constant と let(変数)との違い

三章で詳しく説明されていますが、基本的にRustではグローバル変数をletで作成できません。

なので、Constantなグローバル変数を作りたいときに

const MAX_POINTS: u32 = 100_000;

のような形で宣言するようです。

なのでconstantの一番の違いは、どこでも宣言ができること、のようです。

(違っていたらコメントをください。)

またここでは特に言及されていませんが、mutableなグローバル変数を作るのはなかなか骨が折れそうです。

こちらはまたいずれ調査して、別途まとめたいと思います。

ターミナルからの入力受付と参照渡し

40行目付近、ioライブラリを使って、ターミナルから文字列を受け取っています。

    io::stdin()
        .read_line(&mut guess)
        .expect("Failed to read line");

ここでまず、.(コロン)ごとに改行していますが、これは可読性のためで、別につなげて書いても問題ありません。

次に各関数の説明をしていきます。

        .read_line(&mut guess)

この関数が入力を受け付ける関数になります。

この関数が呼ばれるとターミナルで打ち込まれた文字を読み込みます。

その格納先が、&mut guess になります。

&mut guessは簡単に説明すると、guessという変数(mut)のReference(&)を渡しますよ、ということです。

Referenceというのはなにかというと、C++での参照渡しのようなものになります。

都度都度メモリを確保して値を取り込むのは処理速度的にあまりよろしくないので、Rustでは先に宣言しておいた変数のメモリに直接値を書き込みます。

例外処理

続いて、その下の**.expect()を説明していきます。

    io::stdin()
        .read_line(&mut guess)
        .expect("Failed to read line");

.expect()では、.read_line()からio::Resultという型で帰ってきた実行結果に対して、もしエラーが起きていたらどういう動作をするかを宣言しています。

基本的にStandard ライブラリには中身は違えどこのio::Resultのような結果が格納できる構造体を持っています。

Rustで面白いのは、Cなどではこういった例外処理は宣言する必要がないのですが、Rustではこれを入れないとコンパイル時にワーニングが発報されます。

f:id:ryo_udon:20210131183226p:plain

プログラムが突然落ちることを防ぐためにもいい仕様だなと個人的に思います。

乱数を生成する

チュートリアルこの部分

インポート方法

ここで乱数を生成するために必要なライブラリがrandと呼ばれるライブラリになります。

そのインポート方法を説明していきます。

まず、Cargoではライブラリをバイナリの状態(コンパイルをせずに動く状態)でインポートできることが強みとしています。

なので、まずソースコードにrandを追加するまえに、今回作成したguessing_gameにrandというライブラリをこのプロジェクトに追加していきます。

まずCargo.tomlを開いて、そのdependenciesの部分にrandを追加します。

[dependencies]
rand = "0.5.5"

追加後、以下のコマンドを実行するとライブラリの追加が始まります。

f:id:ryo_udon:20210131183249p:plain

ちなみに cargo updateとうつとdependency内のライブラリをupdateしてくれます。

乱数生成

乱数を生成して、暗証番号を作る機能を加えたのが以下のコードになります。

// std ライブラリ内のio(ファイル入出力)ライブラリをインポート
use std::io;

// 乱数生成
use rand::Rng;

//let mut global = String::new();
//static mut GLOBAL: String = String::new();

fn main() {

    // 文字列を出力するマクロ
    println!("Guess the number!");

    println!("Please input your guess.");

    let secret_number = rand::thread_rng().gen_range(1,101);

    // Point!!
    // letは変数を宣言するよというマクロ
    // mutはこの変数をmutable(後で変更可能)な変数として宣言するよというマクロ
    // Rustの変数はデフォルトはimmutable(後で変更できない, Cのconstかと思ってたが、違うらしい)

    // let foo = 5; // immutable
    // let mut foo = 5; //mutable


    // String::new() String型(文字列型)の空のインスタンス(値を入れる箱)を作成
    let mut guess = String::new();

    // io::stdin()
    //    .readline( &mut guess)
    //ターミナルからのinput を受け付ける関数
    // 引数はmutableな変数へのreference(&) <- C++の参照渡し

    // Point!!
    // .expect("Failed to read line");
    // Rustは関数からの返り値にたいして、

    io::stdin()
        .read_line(&mut guess)
        .expect("Failed to read line");

        println!("You guessed:{}", guess);

    println!("Secret Number was : {}", secret_number);

    //let test = guess;
    //test = "something".to_string();
    //GLOBAL = "2".to_string();
    //println!("Test Global={}", GLOBAL);
}

追加したのは5行目と13行目になります。

5行目はただのインポートの宣言。

13行目が実際に乱数を生成する部分になります。

    let secret_number = rand::thread_rng().gen_range(1,101);

といってもここでは特に説明することはありません。

secret_numberというimmutableな変数に1~101の間でランダムに選ばれた値が代入されています。

入力した値と生成した値を比較する

次に中で生成した暗証番号と入力した数字が一致するのか、はたまた小さいのか大きいのかを判断していきます。

ちなみに値の比較のために以下のライブラリもインポートします。

use std::cmp::Ordering;
    let guess: u32 = match guess.trim().parse() {
        Ok(num) => num,
        Err(_) => {
            println!("You Entered:{}",guess);
        },
    };

    match guess.cmp(&secret_number)
    {
        Ordering::Less => println!("Too Small!"),
        Ordering::Greater => println!("Too Big!"),
        Ordering::Equal => println!("You Win!"),
    }

値の比較とその制限

上のコードで値を比較しているのが以下の部分になります。

guess.cmp(&secret_number)

しかし、ここでguessという変数とsecret_numberという変数の型が一致しないとエラーになります。

(最初に上げたコードのままだと、guessはString型、secret_numberはデフォルトのu32型になります)

そのため、ここではguessという変数をu32型で再度宣言して、trim().parse()という関数を使うことで型変換を行っています。

ちなみにその後のErr(_)は数字以外が来た場合の処理になります。

match

ここでmatchというマクロについて説明します。

例えば上のcmpという関数を使った場合、引数に与えた値に対し、

  1. Ordering::Less -> 小さい
  2. Ordering::Greater -> 大きい
  3. Ordering::Equal -> 一致

という結果を返します。

ここでmatchはこのcmpという関数が上の3つの結果を返してくるというのを先に確認します。

そのため、例えば下のようなコードの場合、分岐に抜けがあるよとエラーを発報してくれます。

    match guess.cmp(&secret_number)
    {
        Ordering::Less => println!("Too Small!"),
        //Ordering::Greater => println!("Too Big!"),
        Ordering::Equal => println!("You Win!"),
    }

f:id:ryo_udon:20210131183400p:plain

入力した数字と生成した数字が一致するまでループを回してみる

最終的なコードがこちらになります。

// std ライブラリ内のio(ファイル入出力)ライブラリをインポート
use std::io;
use std::cmp::Ordering;

// 乱数生成
use rand::Rng;

fn main() {

    // 文字列を出力するマクロ
    println!("Guess the number!");


    let secret_number = rand::thread_rng().gen_range(1,101);
    loop {
        println!("Please input your guess.");
        let mut guess = String::new();
        io::stdin()
            .read_line(&mut guess)
            .expect("Failed to read line");


        //let guess: u32 = guess.trim().parse().expect("Please type a number!");
        let guess: u32 = match guess.trim().parse() {
            Ok(num) => num,
            Err(_) => {
                println!("You Entered:{}",guess);
                continue;
            },
        };
        println!("You guessed:{}", guess);


        match guess.cmp(&secret_number)
        {
            Ordering::Less => println!("Too Small!"),
            //Ordering::Greater => println!("Too Big!"),
            Ordering::Equal => {
                println!("You Win!");
                break;
            }
        }

        println!("Secret Number was : {}", secret_number);


    }
}

loop

C言語でいうwhile(1)に相当するのがこちらのloopになります。

この場合、ループ内でbreakが呼ばれるか、外部からCtrl + Cによるプロセス終了のコマンドが来るまで処理が回り続けます。

実行結果

実行結果が以下になります。

生成した数字と一致したループでbreakが呼ばれ、プロセスが終了していることがわかります。

f:id:ryo_udon:20210131183438p:plain

最後に

今回はチュートリアルの2章部分に少し情報を足した形でメモを残して見ました。

なかなかボリュームがあるので進みが遅いですがめげずにすすめて行きたいと思います。

ではでは。

【Python】【サンプルコード】実際の用途別チートシート を作ってみたよ【複数ファイル、ディレクトリ削除、作成、書き込み、読み込み編】

実際の用途別チートシート を作ってみたよ【複数ファイル削除、作成、書き込み、読み込み(File I/O)編】

概要

たまに使うと忘れがちなファイル書き込み、読み込み

大体そういう時は複数の(大量の)ファイルやディレクトリを作成したり、削除したり、読み込んだりと、

調べるにしてもいくつかに分けて検索する必要があり、意外と大変。

その後にはmatplotlibやpandasも毎回調べるし。

そんなときにとりあえずコピペしておけば動くそんなコードをまとめたシートになります。

なお、ここでは一機能の説明ではなく、実際の用途ごとにまとめてあります。

各関数ごとの細かい機能は下の関数ごとのリンク先か別途Google先生で確認してください。

随時更新中

このチートシートで使われているライブラリ一覧

ファイル、ディレクトリ探索

glob

ファイル、ディレクトリ存在確認

os.path系

ディレクトリ作成

os.mkdir

ディレクトリ削除

os.remove

複数(大量)の同じ名前のファイルを作成

データをロギングする、タイムスタンプで区切って別々のファイルに分割する。

意外と使うことが多い、ファイル名 = 名前+インデックス+拡張子(.csvなど) での複数(大量の)ファイル作成

そのときは以下のコードを使いましょう。

path = "./data/"
filename = "sample"
filetype = ".csv"
filenum = 10

for i in range(filenum):
    open_filename = path + filename + str(i) + filetype
    with open(open_filename, mode='w') as f:
        f.write("1,1,1,1")

これであなたも大量にファイルを製造できちゃう!!

複数(大量)の同じ名前のファイルを作成(ヘッダー付きCSV)

ちなみにCSVファイルを作る際には各行の最後に","をつけたくなりますが、つけないことをオススメします。

それはpandasで読み込むときに無駄に1列、列に追加されてしまうからです。

path = "./data/"
filename = "sample"
filetype = ".csv"

header = "timestamp, data1, data2\n"

for i in range(10):
    open_filename = path + filename + str(i) + filetype
    with open(open_filename, mode='w') as f:
        f.write(header)
        for j in range(10):
            timestamp_str = str(j)
            str_data = timestamp_str + ",1,1"
            f.write(str_data)
            f.write("\n")

複数(大量)の同じ名前のディレクトリ(フォルダ)にファイルを作成する場合

正直インデックスで分ければいいじゃんと思うかもしれないが、たまに水準を振って、同じファイル名で保存したいときがある。

そんなときはファイル名はそのままで、複数のディレクトリを作成し、分けて入れてあげましょう。

import os

path = "./data/"
filename = "sample"
filetype = ".csv"

header = "timestamp, data1, data2\n"

for i in range(10):
    dir_path = path + filename + str(i)
    if os.path.exists(dir_path) == False:
        os.mkdir(dir_path)

    open_filename = dir_path + "/" + filename + filetype
    with open(open_filename, mode='w') as f:
        f.write(header)
        for j in range(10):
            timestamp_str = str(j)
            str_data = timestamp_str + ",1,1\n"
            f.write(str_data)

複数(大量)の同じ名前のファイルを削除

逆に大量のファイルを通流前は一度ディレクトリの中をきれいにしたくなりますよね。

そんなときは以下のコードを使いましょう。

import os
import glob

path = "./data/"
file_type = ".csv"
filenames = glob.glob(path + "*" + file_type)

for filename in filenames:
    os.remove(filename)

指定ディレクトリ以下の同じ拡張子のファイルを削除する

複数のディレクトリに渡ってデータを保存しているとき、一度きれいにしたくなることがありますよね。

そんなときは以下のコードを実行しましょう。

import os
import glob

path = "./data/"
file_type = ".csv"
filenames = glob.glob(path + "**/*" + file_type)
for filename in filenames:
    os.remove(filename)

指定ディレクトリ以下のすべてのファイル、ディレクトリを削除する

とりあえず問答無用で全部削除してまっさらにしたい!! そんなときありますよね。

そんなときは以下のコードを実行しましょう。

import shutil
import os

path = "./data/"
if os.path.exists(path):
    shutil.rmtree(path)
os.mkdir(path)

CSVファイルを読み込んで配列に格納する

とりあえず文字列でいいからデータの中身を確認したい。

そんなときは以下のコードを使ってください。

path = "./data/"
filename = "sample0.csv"

with open(path + filename, mode='r') as f:
    line = f.readline()
    while line:
        line = line.replace('\n','')
        str_data = line.split(',')
        print(str_data)

        line = f.readline()

CSVファイルを読み込んで数値として配列に格納する

数値として配列に格納してささっとグラフを書きたい。

そんなときは以下をどうぞ。

path = "./data/"
filename = "sample0.csv"

with open(path + filename, mode='r') as f:
    line = f.readline() # headerをスキップ
    line = f.readline()
    while line:
        line = line.replace('\n','')
        str_data = line.split(',')

        data = [int(x) for x in str_data]
        print(data)

        line = f.readline()

【Rust】【チュートリアル】世界のトヨタが使っているというRustを使ってみたよ、その1

Rustを使ってみたよ、その1

概要

最近、自動運転界隈で注目されているトヨタが開発中のCarOS(Arene)がRustという言語で開発されているということを聞きつけ、

ミーハーな自分を抑えられなかったので簡単に触ってみました。

かなりしっかりしたチュートリアルがあるので、現在はこれを勧めている段階のメモ書きになります。

今回はRustのインストールと簡単なHello Worldの実行方法です。

なにか間違っている箇所などがあれば、教えていただければ嬉しいです。

そもそもRustってなに?

昔から存在は知っていましたが、正直、僕も最近になって世界中で使われていることを知りました。

公式サイトには、以下のようにあります。

www.rust-lang.org

Hundreds of companies around the world are using Rust in production today, like Firefox, Dropbox, and Cloudflare, uses Rust. From startups to large corporations, from embedded devices to scalable web services, Rust is a great fit.

FireFoxDropboxでも使われているのは知らなくてびっくり。

なぜそんな企業に選ばれているのか、その理由は以下のようです。

for fast, low-resource, cross-platform solutions.

実行速度が早く、大きなリソースを必要とせず、さらにクロスプラットフォームなソフト(Windows, Mac, LinuxなどOSが変わっても実行できるソフト)を作成できることが大きいようです。

また、その他にも下のように、メモリ、スレッドセーフ、メモリや処理の効率化、などこれまでの研究向けな言語とは違い、より現場向きな言語なのかなという印象です。

f:id:ryo_udon:20210124151733p:plain

というわけで使ってみた。

Rustはチュートリアルを始めてとしたドキュメントがかなり充実しているので、簡単に情報を集めることができます。(まだ始めたばかりなので、いずれ印象が変わるかも)

なので、今回からチュートリアルをやってみましたの備忘録から始まり、なにか自分でアプリをRustで書いてみたいと思います。

Rust のインストール

今回は手元のMacではなく、以前購入したRaspberry Pi 4Bにインストールしてみました。

ryo-udon.hatenadiary.jp

インストールはこちらを参考にしています。

curlのインストール

まず、curlがインストールできていなかったので、インストールしていきます。

sudo apt-get Update

sudo apt-get isntall curl

Rustのインストール

次のコマンドを打ちます。

curl --proto '=https' --tlsv1.2 https://sh.rustup.rs -sSf | sh

これでEnterを推すと、途中で下の画像のようにDefault Installかを確認されますので、

1

と打ってEnterを押すとインストールが開始します。

[f:id:ryo_udon:20210124150205p:plain]

インストールが終わると以下のように完了の文字がでます。

[f:id:ryo_udon:20210124150222p:plain]

しかもその下にpath設定の必要があるけど、次回起動時に勝手にやってくれるよとのメッセージが。なんて親切。

なお、この際に公式の手順を見るとC言語コンパイラのインストールを推奨しているようです。

Additionally, you’ll need a linker of some kind. It’s likely one is already installed, but when you try to compile a Rust program and get errors indicating that a linker could not execute, that means a linker isn’t installed on your system and you’ll need to install one manually. C compilers usually come with the correct linker. Check your platform’s documentation for how to install a C compiler. Also, some common Rust packages depend on C code and will need a C compiler. Therefore, it might be worth installing one now.

自分はgcc, g++をすでにインストールしているので、問題なく動きました。

これでインストールは完了です。

開発環境

まだ触り始めたばかりで今後色々と設定を加えていくとは思いますが、今はこれまで通りVSCodeで書いています。

(リモートアクセスが簡単にできて、ターミナルも自動でsshしてくれるので)

なのでVSCodeRustのExtensionを追加だけして作業をしています。

ちなみに他にもIDEに対応しているようなので、お好みのIDEで開発してください。これのほうが便利などの情報もお待ちしています。

[f:id:ryo_udon:20210124150349p:plain] リンク

個人的にGeanyとかまでちゃんと対応しているのに驚きました。使ってる人いるんだ。。。

Hello Rust World!!

workspaceの作成

ではプログラムではおなじみ、"世界よ、こんにちは"の時間です。

まずはworkspaceを作っていきます。

ターミナルを開き、以下を入力していきます。

mkdir rust_workspace
cd rust_workspace
mkdir hello_world
cd hello_world

vscodeを使っている人は右側のFolderからworkspace、.rsファイルを作っていただいて大丈夫です。

ちなみにご存知の方が多数だと思いますが、"mkdir"がディレクトリ(Windowsでいうフォルダ)を作成するコマンド、"cd"が指定したディレクトリに移動するコマンドになります。

プログラムを書いていく

hello_worldのディレクトリの中でプログラムを書いていきます。

まずはhello_worldの中に"main.rs"というファイルを作っていきます。

ここで、"main.rs"の"main"の部分は何でも構いません。

"main"でもいいですし"a"でも構いません。

次に作った.rsファイルを編集していきます。

自分の好きなIDEで開いて以下のように編集してください。

fn main()
{
    println!("Hello Rust World!");
}

編集が終わったら保存をして、hello_worldのディレクトリをターミナルで開きます。

そして以下のコマンドを叩いていきます。

rustc main.rs

これがRustのコンパイルコマンドになります。

これを実行すると同じディレクトリ内に名称が同じの実行ファイルが生成されます。

ここまで来たら、後は実行するだけです。

./main

すると以下のような実行結果が得られます。

f:id:ryo_udon:20210124150429p:plain

ようこそ、Rustの世界へ!!

Cargoを使っていく

cargo new (Package作成)

さてこれでRustのプログラムは書けたわけですが、実はこの方法は公式推奨の方法ではありません。

公式では"Cargo"と呼ばれる、C, C++でいうCMakeのような外部のライブラリやパッケージのDependency(依存関係)を記載することができます。

なので、実は上で実際に書いたことがコマンド3つで実行できます。

まずrust_workspaceにもどって以下のコマンドを実行します。

cargo new hello_cargo

これでhello_cargo というcargoを使うためのディレクトリが作成されました。

また、その中を覗いてみると、 hello_cargo/ ├── Cargo.toml └── src └── main.rs

が自動で生成されています。

ここでさらにsrcディレクトリの中を覗いてみると、main.rsという先ほど作ったのと中身まで同じファイルが生成されています。

ここでCargo.tomlファイルというのがmakefileのようなものでpackage名やversion, dependencyなどを設定することができます。

cargo build (ビルド、コンパイル)

ここまで見たらまたまたターミナルに戻り、以下のコマンドを実行します。

cargo build

これでCargo.tomlの指示通りにビルド、コンパイルをしていきます。

これが終わり、またまたディレクトリ内を確認すると、今度はtargetというディレクトリができています。

その下も覗いてみると大体このような形になっています。

hello_cargo/
├── Cargo.lock
├── Cargo.toml
├── src
│   └── main.rs
└── target
    ├── .rustc_info.json
    ├── CACHEDIR.TAG
    └── debug
        ├── build
        ├── deps
        ├── examples
        ├── hello_cargo
        ├── hello_cargo.d
        └── incremental

cargo run (実行)

最後に実行です。

実行ファイルは"target/debug/hello_cargo"なので、

./target_debug/hello_cargo

でも実行できますが、もちろん実行コマンドが準備されています。

hello_cargoディレクトリで以下のコマンドを実行してください。

cargo run

すると実行すべき実行ファイルを勝手に探して、実行してくれます。

f:id:ryo_udon:20210124150529p:plain

そのほかのコマンド

このcargoには他にもコマンドがあるようで、個人的にお、いいなと思ったのが真っ先に紹介されていた"cargo check"コマンドです。

プログラムを書いた人なら夢でうなされたことがあるであろう、Segmentation Fault, コアダンプ。 これらが起きないかを確認してくれるコマンドになります。

まだこちらは試せていないので、色々と作っていく中で紹介できればなと思います。

まとめ

とりあえずRustを始めてみました。

特に何を作りたいというわけではありませんが、どういうことができるのか知りたいのでこれで色々とつくっていければなと思います。

おそらく定期的に更新していくと思うのでお楽しみに。

ではでは。

M1 MacBook Proを買ってみたよ

M1 MacBook Proを買ってみたよ

概要

現在使っているMacBook(2013年モデル)を使い続けてはや6年。

まだまだ現役で使えるけど、そろそろ心機一転で買い替えてみようかな、と思い、最近話題になっているM1 MacBookProを購入しました。

なので、その使用感やセットアップでのコツのようなもの、最終的に今買うべきかを話していきます。

f:id:ryo_udon:20210123164126j:plain

外観

本体

外観はこんな感じです。

f:id:ryo_udon:20210123164201j:plain

もともと持っているMacBookと比べると一回りぐらい小さくなっています。

f:id:ryo_udon:20210123164313j:plain

また薄さもかなりのもの。

f:id:ryo_udon:20210123164332j:plain

重さについても、持ってみた感じ2,3割ぐらい軽くなってそうです。

調べてみたらこのようになっていました。左が2013年モデル、右が2020lateモデルです。

(意外と自分の感覚も馬鹿にできない笑)

f:id:ryo_udon:20210123164547p:plainf:id:ryo_udon:20210123164555p:plain

このスペックは公式サイトから抜粋しています

2013モデル 2020モデル

キーボード

数年前から採用されているタッチバーは健在。ただ、以前買うことを断念した理由の一つ、Escキーがタッチパネルだった問題は、今のモデルでは改善されて普通の物理ボタンになっています。

押し心地も薄い割にストロークもありいい感じです。

f:id:ryo_udon:20210123164201j:plain

セットアップ

とりあえず、過去のMacBook Proからデータをそのまま引き継ぐためにTime Machine(Macにデフォルト搭載されているバックアップソフト)を使ってみました。

やり方はこちらを参考にしてください。

自分の場合、3時間ぐらいかかりました。

f:id:ryo_udon:20210123164749j:plain

ただ、後で話しますが、この方法は絶対にやらないほうがいいです!!

あれ?処理が速いって話じゃ。。。

試しにGoogle ChromeYoutubeでも見ようかと思ったら、なぜかもっさり。。。

何なら前のMacBook Proのほうが速い。。。

なぜだ?と思い調べたら、Intel Core のMacBook Pro からM1 チップにデータを引き継ぐと、Intel用のソフトがそのまま入ってしまうそうです。

とはいえ、世の中のソフトはIntel想定のものばかりのため、そこをカバーするためにRosetta2と呼ばれる翻訳ソフトを介して処理を行います。

なので、Google Chromeなど、すでにM1 チップに対応しているソフトはM1用のものをインストールしないとかなりもっさりしてしまいます。

また、その他にも長年Macを使っているとバックグラウンドで動くソフトがいくつか入っているもので、それによって全体の操作がもっさりしてしまうという事態が発生してしまいます。。。

なので、上でも書いたように Intel -> M1に移行する場合はTime Machineでの移行はオススメしません!!

え? MacBook Proなのに?

ここで白状しますが、実は今回購入したMacBook Pro、自分は返品しました。

その理由は、

"外部ディスプレイが一枚しか繋げないから!!"

今自分のデスクでは、持ち運ぶとき以外は基本MacBookを閉じたままで、ディスプレイアームで支えられたモニタ二枚で作業しています。

そんなデスク環境で一枚しか繋げないのはつらすぎる。。。

f:id:ryo_udon:20210123164943p:plain

僕の中でMacBook Pro, Airの違いはつなげる外部ディスプレイの数だったのですが(7年前の情報)、いつの間にかそれも変わってしまったようです。

Intel Coreであれば今ではAirでも外部ディスプレイ二台につなげることができるようです。

ちなみにカスタマーサポートにこれがM1用のOS側の原因かそれともハード側に起因するものなのか確認したところ、もちろんハードの問題のため、今後その台数が増えることはないと回答されました。

うーん。残念。

結論

というわけで操作感やサイズ感などはすごく良かったのですが、残念ながら自分の使用用途とは合わなかったので、今回は泣く泣くキャンセルとなりました。

ちなみに、こんなM1 MacBookでも実は複数台の外部ディスプレイにつなぐ方法が(非公式ですが)あります。

それが、DisplayLink Docking Station。

調べたところDisplayLinkと呼ばれる認証を受けたDocking Station(下の画像のようなものです)ではドライバなどを追加でインストールする必要がありますが、

これをつなぐことで複数台(中には6台もつないでいる猛者もいたようです)につなげるようです。

探した動画の中でかなり詳しく説明されていました。 youtu.be

六台もつないだ猛者がこちら。 youtu.be

ただ、金額が1万円以上するので、これ以上出費する気にならなかったので、ちょっと今回は返品して後1,2年様子を見ようと思っています。

やはりパソコンなどはこういうこともあるから返品対応してくれるメーカーがやっぱりいいですよね。

ちなみにAppleは届いてから一週間以内はヤマト運輸のみですが、送料無料で送り返すことができるのでその点も安心です。

ではでは。

/*コードブロックに言語名を表示*/ pre.code:before { content: attr(data-lang); display: inline-block; background: white; color: #666; padding: 3px; position: absolute; margin-left: -10px; margin-top: -30px; } pre.code { padding-top: 30px !important; }