Visual Studio 2017でTypeScriptのコンパイルを無効にする
ASP.NET MVC + TypeScriptでwebを書いていたんですが、 サーバー側の処理だけ動作確認したいっていうときにTypeScriptのソースにエラーが出ているとデバッグができないですよね。
webpack動かせばちゃんとエラーは分かるし、TypeScriptはvscodeで書くからVisual Studio上のコンパイルは無効にしちゃっていいんじゃないかなと。
無効にする方法
.csproj
ファイルのPropertyGroup
内に以下の記述を追加
<TypeScriptCompileBlocked>true</TypeScriptCompileBlocked>
これでVisual Studio上でTypeScriptのコンパイルが無効になります。
累積和
競プロで知ったことのメモ。
簡単な概要
累積和とは前処理をすればの計算量で、区間の合計を求めることができるアルゴリズム。
例えば、長さの数字の配列があるとする。
2 | 6 | 1 | 9 | 5 | 8 | 4 |
から までの区間の合計を求めたいというときは、単純にからまでをループ()すれば求めることができる。
配列が長かったり、~ 、~ 、~ .....と区間の合計を求める回数が例えば回もある場合、かかるので処理にとても時間がかかってしまう。
累積和を使うと前処理にかければ、区間の合計を求める計算量はで済む。
どんな前処理かというと、以下のような大きい配列を用意する。
0 | 2 | 8 | 9 | 18 | 23 | 31 | 35 |
この配列の中身、にはまでの区間の合計が入っている。
- ...
- ...
- ...
この配列を用意すれば、から までの区間の合計はという式で求めることができる。
にはからの合計が入っているので、からの合計が入っているを引く。
となる。
実装
上の例、から までの区間の合計を求めるプログラムをC#で実装してみる。
void Sample() { var N = 7; var a = new int[7] { 2, 6, 1, 9, 5, 8, 4 }; var s = new int[N + 1]; var left = 2; var right = 6; for (int i = 0; i < N; i++) { s[i + 1] = s[i] + a[i]; } Console.WriteLine(s[right + 1] - s[left]); }
webpackを使ってみる
前回、Babelについて書きました。
今回はwebpackの使い方をメモ程度のクオリティで書いていきます。
webpackとはなにか
モジュールバンドラというものです。
モジュールバンドラとは複数のJavaScriptファイルをまとめて1つのファイルにするツールのことです。
複数のJavaScriptファイルを1つにまとめると、どんな良いことがあるでしょうか?
- jsファイルの読み込み順など気にしなくても勝手に依存関係を解決してファイルをまとめてくれる。恐らくこれが1番のメリット。
- ファイルをまとめることでリクエスト数を減らす。通信回数を減らすことができる。
- 実行時は1つにまとめるので、気にせず機能ごとにファイルを分割してコーディングできる。 変数名が被っても大丈夫。
などなど、メリットがあります。
ディレクトリ構造
今回は以下のようなディレクトリ構造で進めていきます。
. ├── src │ ├── app.js │ ├── factorial.js │ └── fizzbuzz.js │ ├── dist │ └── bundle.js ├── node_modules ├── package.json └── webpack.config.js
webpackの環境構築
以下の環境でやってきます。
$ node --version v10.15.0 $ npm --version 6.5.0
まずはpackage.jsonを作ります。
npm init -y
必要なパッケージをインストール。
npm install --save-dev webpack webpack-cli
webpack.config.js
というwebpackの設定ファイルを作ります。
const path = require('path'); module.exports = { mode: 'development', entry: './app.js', output: { filename: 'bundle.js', path: path.join(__dirname, 'dist') }, };
- mode:
development
,production
,none
がある。development
: 開発用。ビルド時間が短い。production
: 本番用。ビルド時間が長い。最小化されたファイルが出力される。none
: オプション無効。
- entry: エントリーポイントの設定。起点となるjsファイルを指定する。 webpackはこのファイルが依存しているモジュールを調べてバンドル(まとめる)してくれる。
- filename: 出力ファイル名
- path: 出力先のパス
これで最低限のwebpackの環境ができました。
サンプルのコード
バンドルするJavaScriptを用意します。
サンプルでfizzbuzzと階乗を求めるモジュールを作りました。
これをapp.js
上で呼び出します。
fizzbuzz.js
export const fizzbuzz = x => { let result = ''; if (x % 3 == 0) result += 'Fizz'; if (x % 5 == 0) result += 'Buzz'; return result || x; };
factorial.js
export const factorial = x => { if (x === 0) return 1; return x * factorial(x - 1); };
app.js
import {fizzbuzz} from './fizzbuzz'; import {factorial} from './factorial'; console.log(fizzbuzz(3)); console.log(fizzbuzz(5)); console.log(fizzbuzz(15)); console.log(fizzbuzz(98)); console.log(factorial(5));
バンドルしてみる
npxコマンドを使用してwebpackを実行します。
npx webpack
npxが使えない環境は
./node_modules/.bin/webpack
実行結果
$ npx webpack Hash: 9ca0da58fd666451390a Version: webpack 4.29.6 Time: 99ms Built at: 03/21/2019 12:07:03 AM Asset Size Chunks Chunk Names bundle.js 5.62 KiB main [emitted] main Entrypoint main = bundle.js [./app.js] 211 bytes {main} [built] [./factorial.js] 107 bytes {main} [built] [./fizzbuzz.js] 156 bytes {main} [built]
特にエラーがなければdist
フォルダの中に1つにまとめられたbundle.js
ができています。
bundle.js実行
$ node dist/bundle.js Fizz Buzz FizzBuzz 98 120
app.js
上で呼び出したモジュールがまとめられ、ちゃんと実行できています。
webpack&Bebelで使う
次にwebpackとBabelと組み合わせて使ってみます。
必要なパッケージのインストール
$ npm install --save-dev babel-loader @babel/core @babel/preset-env
- babel-loader: webpackでBabelを使うのに必要なパッケージ
webpack.config.js
を以下のように書き換えます。
const path = require('path'); module.exports = { mode: 'development', entry: './src/app.js', output: { filename: 'bundle.js', path: path.join(__dirname, 'dist') }, // ↓以下追加 module: { rules: [ { // 処理対象ファイル test: /\.m?js$/, // 処理の対象外にするディレクトリ exclude: /node_modules/, use: { loader: 'babel-loader', options: { presets: ['@babel/preset-env'] } } } ] } };
module
というのを追加しました。
ここは「jsファイルはBabelで変換するように。ただし、node_modulesディレクトリ内のjsファイルは無視してね。」という指示を書いています。
node_modulesを除外しないと変換の速度が遅くなったり、エラーになったりするようです。
Babelの設定はbabel.config.js
などに書くのですが、webpack.config.js
のoptions
に書いてしまうこともできます。今回はこっちに設定を書きました。
Babelとwebpackを連携させる設定ができたので、
あとはwebpackを実行すればトランスパイルされているbundle.js
が出力されます。
modeをnoneにすると変換したところがわかりやすいのでnoneで実行します。
$ npx webpack --mode=none
bundle.jsの中身抜粋
/***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "fizzbuzz", function() { return fizzbuzz; }); var fizzbuzz = function fizzbuzz(x) { var result = ''; if (x % 3 == 0) result += 'Fizz'; if (x % 5 == 0) result += 'Buzz'; return result || x; }; /***/ }), /* 2 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "factorial", function() { return factorial; }); var factorial = function factorial(x) { if (x === 0) return 1; return x * factorial(x - 1); }; /***/ }) /******/ ]);
わざとらしくアロー関数で書いたとこがトランスパイルされているのを確認できました!
おわり。
Babelを使ってみる
Babel、どんなツールか知っていたけれど使ったことなかったので試してみます。
webpack + Babelで使うのをよく見ますが、とりあえずBabelだけコマンドラインから使ってみます。
Babelとはなにか
簡単に言うと、新しいJavaScriptの書き方で書いたコードを古い書き方のコードへ変換するツールです。
なぜせっかく新しい構文で書いたのにわざわざ古い方へ変換しなきゃいけないかというと、
ブラウザごとにJavaScriptの新バージョンへの対応状況が違うので、新しい書き方だと動いてくれないブラウザがあるからです。(※IEとかIEとかIEとかね)
ということで、Babelを使って変換すればブラウザが対応してない構文でも気にせずコーディングできます。
Babelの環境構築
実行環境
- Node.js: 11.10.1
- npm: 6.7.0
npmで必要なパッケージをインストールします。
インストールする前にnpm init -y
でpackage.json
を作っておきます。
$ npm install --save-dev @babel/core @babel/cli @babel/preset-env
設定ファイル
babel.config.js
というファイルを作って、ここにBabelの設定を書いていきます。
const presets = [ ['@babel/preset-env', { 'targets': { 'ie': '11', 'chrome': '58' } }] ]; module.exports = { presets }
使用するプリセット(@babel/preset-env
)と、targets
にサポートするブラウザのバージョンを指定します。
指定した環境を最低限サポートするよう変換してくれます。
Babel実行
試しに以下のJavaScriptを変換してみます。
どれもIE11では対応していないECMAScript2015の構文で書いてみました。
index.js
const add = (a, b) => a + b; class Person { constructor(age, name) { this.age = age; this.name = name; } toString() { return `name=${this.name}, age=${this.age}`; } } const numbers = [1, 2, 3, 4, 5]; for (const number of numbers.map(x => x * 2)) { console.log(number); }
次にpackage.json
にBabelの実行コマンドを登録しておきましょう。
ここでは、src
というフォルダに先程のindex.js
を用意して変換してみます。
-o
オプションで変換後のファイルを出力できます。dist
フォルダに出力することにします。
"scripts": { "build": "./node_modules/.bin/babel src/index.js -o dist/index.js" },
保存したらpackage.json
がある場所で
$ npm run build
変換後のindex.js
"use strict"; function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; } var add = function add(a, b) { return a + b; }; var Person = /*#__PURE__*/ function () { function Person(age, name) { _classCallCheck(this, Person); this.age = age; this.name = name; } _createClass(Person, [{ key: "toString", value: function toString() { return "name=".concat(this.name, ", age=").concat(this.age); } }]); return Person; }(); var numbers = [1, 2, 3, 4, 5]; var _iteratorNormalCompletion = true; var _didIteratorError = false; var _iteratorError = undefined; try { for (var _iterator = numbers.map(function (x) { return x * 2; })[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) { var number = _step.value; console.log(number); } } catch (err) { _didIteratorError = true; _iteratorError = err; } finally { try { if (!_iteratorNormalCompletion && _iterator.return != null) { _iterator.return(); } } finally { if (_didIteratorError) { throw _iteratorError; } } }
はい。ちゃんとアロー関数やクラス、for ofループが変換されています。
for ofループのとこなんかぱっと見た感じ何やってんのか分からないレベルですね...
他にもいろいろ設定があったりするのですが、とりあえずES2015の構文が変換できればいいということで終わりにします。
Raspberry PiにDockerで.NET Core環境を構築してみた
ラズパイでC#を動かしたくて ラズパイ内に.NET Core環境をDockerで構築してみました。
バージョン
- .NET Core SDK 2.1
ラズパイの環境
- Raspberry Pi 3 Model B
- Raspbian 9.1
Dockerインストール
まずはDockerをインストールします。
以下のコマンド1つでインストールできました。
$ curl -sSL https://get.docker.com | sh
$ docker --version Docker version 18.06.0-ce, build 0ffa825
これでDockerはインストールできましたが、dockerコマンドがroot権限がないと使えないので以下のコマンドでdockerグループにユーザーを追加します。
$ sudo usermod -aG docker ユーザー名
これでsudoを打たなくてもdockerコマンドを実行することができます。
.NET Coreが動作するコンテナの準備
次は.NET Coreが動作するDockerコンテナを立てます。
色々調べてたらラズパイで動きそうなarm32v7対応のDockerfileが公式にありましたのでこれを使いました。
※追記 Dockerfileのリンクが切れていたので更新しました。
バージョンは3.1です。
Dockerfileのビルド
先程のURLのDockerfileを使ってビルドし、イメージを作ります。
$ docker build -t dotnet .
コンテナの起動
$ docker run -it --name my-dotnet dotnet
コンテナを起動できたらdotnetコマンドを実行してみます。
# dotnet --version 2.1.302
.NET Coreがちゃんとインストールできているのが確認できました。
サンプルプログラムを動かす
.NET Coreの環境ができたのでサンプルプログラムを動かしてみます。
プロジェクト作成
dotnet new
コマンドでプロジェクトを新規作成します。
-o
オプションで出力するプロジェクトの配置場所を指定。
今回はコンソールアプリケーションを実行したいのでconsole
を指定しました。
作成できるテンプレートはdotnet new -l
で確認できます。
# dotnet new console -o Sample The template "Console Application" was created successfully. Processing post-creation actions... Running 'dotnet restore' on Sample/Sample.csproj... Restoring packages for /root/Sample/Sample.csproj... Installing Microsoft.NETCore.DotNetAppHost 2.1.0. Installing Microsoft.NETCore.DotNetHostResolver 2.1.0. Installing Microsoft.NETCore.DotNetHostPolicy 2.1.0. Installing NETStandard.Library 2.0.3. Installing Microsoft.NETCore.Platforms 2.1.0. Installing Microsoft.NETCore.Targets 2.1.0. Installing Microsoft.NETCore.App 2.1.0. Generating MSBuild file /root/Sample/obj/Sample.csproj.nuget.g.props. Generating MSBuild file /root/Sample/obj/Sample.csproj.nuget.g.targets. Restore completed in 18.24 sec for /root/Sample/Sample.csproj. Restore succeeded.
実行すると-o
で指定したディレクトリの中にこんな感じでファイルができています。
# ls Program.cs Sample.csproj obj
Program.csの中身
using System; namespace Sample { class Program { static void Main(string[] args) { Console.WriteLine("Hello World!"); } } }
それではdotnet run
コマンドでプログラムを実行してみましょう。
コマンドを実行する前に.csproj
ファイルがあるディレクトリまで移動します。
もしくは--project
オプションでプロジェクトを指定することもできます。
# cd Sample # dotnet run Hello World!
実行までに多少時間がかかるけど正常に動いてくれました!
参照渡し?値渡し?
よくわかっていなかったので調べました。
参考記事
値渡し
C#(というか多くの言語)では通常、メソッドの引数へは値渡しとなる。
値渡しとは変数の値をコピーしてメソッドの引数に渡すこと。
なので値を渡した変数をメソッド内で変更しても、元の変数は変化しない。
public static void Add(int x) { x += 10; Console.WriteLine(x); // 10 } static void Main(string[] args) { int x = 10; Add(x); Console.WriteLine(x); // 10 }
参照の値渡し
先ほどの例は値型だが、 参照型をメソッドに渡すときも値渡しである。
参照型の場合、変数の参照値をコピーしてメソッドの引数に渡している
これを参照渡しと間違いやすいようだが、実際には参照の値渡しである。
メソッドの変数には呼び出し元の変数を指す参照値が入っているので、Add
すると呼び出し元の変数もorange
が追加される。
public static void Foo(List<string> words) { words.Add("orange"); words.ForEach(Console.WriteLine); } static void Main(string[] args) { var words = new List<string> { "apple", "banana" }; Foo(words); Console.WriteLine("-----"); words.ForEach(Console.WriteLine); }
出力結果
apple banana orange ----- apple banana orange
words
に入っているのはあくまでも参照値のコピーなので、メソッド内で新たなインスタンスを代入し、操作を行っても呼び出し元には影響しない。
public static void Foo(List<string> words) { words = new List<string>(); words.Add("orange"); words.ForEach(Console.WriteLine); } static void Main(string[] args) { var words = new List<string> { "apple", "banana" }; Foo(words); Console.WriteLine("-----"); words.ForEach(Console.WriteLine); }
出力結果
orange ----- apple banana
参照渡し
PythonやJavaには値渡しのみで参照渡しはないが、C#にはちゃんと参照渡しが存在する。
参照渡しは変数の参照をメソッドの引数に渡している。※コピーではない
参照の値渡しでは不可能だったが、参照渡しは呼び出し元の変数をメソッド内で操作するのと同じことなので、新しいインスタンスを代入するとそれも呼び出し元に反映される。
呼び出し元、メソッドの引数の前にref
を指定することで参照渡しになる。
public static void Foo(ref List<string> words) { words = new List<string>(); words.Add("orange"); words.ForEach(Console.WriteLine); } static void Main(string[] args) { var words = new List<string> { "apple", "banana" }; Foo(ref words); Console.WriteLine("-----"); words.ForEach(Console.WriteLine); }
出力結果
orange ----- orange
終わり。