【アルゴリズム(JS実装)】Selection Sort
どうも、わくばです!
今日はSelection Sort を学びました。今回はシンプルです。
アルゴリズムの説明はこのサイトが非常にわかりやすいのでぜひ:Selection Sort - InterviewBit
コードはこんな感じになりました。
function selectionSort(array, size){ const { length } = array; for (let i=0; i< length; i++){ let minIdx = i; for(let j =i+1; j < length; j++){ if(array[i]>array[j]){ minIdx = j; } } [array[i], array[minIdx]] = [array[minIdx], array[i]]; } return array; }
実行してみると
let data = [-2, 45, 0, 11, -9] selectionSort(data, data.length)
[-9, -2, 0, 11, 45]
機能しています。
Complexityはスワップし終えたものは比較対象から減っていくので
$$(n-1)+(n-2)+...+2+1=\dfrac{n(n-2)}{2}$$
これは概算すると\(O(n^2)\)になります。ループが二重なので直感的にも納得です。(Big O notationを知らない方のために一応コメントしておくと、Big O notationはあくまで複雑性の指標であって正確な計算値ではありません。処理の複雑さ的に\(O(n^2)\)とすると定性的に理解できて良いですね、というものです。)
今日は以上です。E資格の勉強引き続き頑張ります。あ、あと医学も!(笑)
では。
【アルゴリズム(JS実装)】Binary Heap
どうも、わくばです!
今日はBinary Heapです。正直コードに落とすのがかなり複雑で時間がかかっていしまいました。ヒープがどんなデータ構造なのかについてはページの最後に参考を用意しておいたのでそちらを御覧ください。*1
結局は優先キューなので、「自由に追加できる」「先頭から最小のものを取り出せる」が実現できれば良く、重要なのは挿入や抽出をした後の配置換えの部分になるかなと思います。以下では一つのクラスとしてまとめて実装しています。
Minimum HeapとMax Heapどちらでも大した違いはありませんが、今回はMinimum Heapを実装しました。
class MinHeap { constructor(){ this.heap = [] } //配列がどうなっているか見るためのメソッド //アルゴリズムには不要 checkHeap(){ return this.heap; } //入れ替えるメソッド swap(array,a,b){ [array[a], array[b]] = [array[b], array[a]]; } //index番目の要素について、左下の子ノードのインデックスを取得 getLeftIndex(index){ return 2*index+1; } //右下の子ノードについても同じ様に getRightIndex(index){ return 2*index+2; } //index番目の要素の直上の要素(親ノード)のインデックスを取得 getParentIndex(index){ return index!==0 ? Math.floor((index-1)/2) : undefined; } //ヒープの末尾に追加するメソッド //成功したらtrueを返す insert(value){ if (value != null) { this.heap.push(value); //あとで定義している、ふるい上げメソッド this.siftUp(this.heap.length - 1); return true; } return false; } //末尾に追加された値を適切な位置までふるい上げる siftUp(index) { let parent = this.getParentIndex(index); while ( index > 0 && this.heap[parent] > this.heap[index] ) { this.swap(this.heap, parent, index); index = parent; parent = this.getParentIndex(index); } } //ヒープの先頭を取り出す(MinHeapでは最小値が取得できる) extract() { //ヒープの配列自身が空ならundefined if (this.heap.length === 0) { return undefined; } //要素がひとつならそいつを返す if (this.heap.length === 1) { return this.heap.shift(); } //上記以外は先頭を切り取る const removedValue = this.heap.shift(); //あとで定義してある、ふるい下げメソッド //先頭が抜けたことで配置換えを行う this.siftDown(0);
return removedValue; } //ふるい下げるメソッド siftDown(index) { //抽出した要素のインデックスをindexとして受ける。 //extractメソッドで使われていて実引数0が入ってくる //elementは交換先を保持する変数とする。初期値は自身 let element = index //便宜的に変数を作っているだけ。わざわざしなくても可能 const left = this.getLeftIndex(index); const right = this.getRightIndex(index); const size = this.heap.length; /** まず左の子ノードから評価し、親>子ならelement(交換先)をleftに変更 次に右でelement(=left)と比較することで、親、右子、左子の中で最小のものを入れ変えられる **/ if ( left < size && this.heap[element] > this.heap[left] ) { element = left; } if ( right < size && this.heap[element] > this.heap[right] ) { element = right; } //elementが更新されていればそれに応じて入れ替える。 if (index !== element) { this.swap(this.heap, index, element);
//入れ替え後はelementはまだ交換先の情報が残っているのでそれを使って再帰的にふるい下げていく this.siftDown(element); } } }
こんな感じです。ふるい上げの方は自身と親を比べるだけなのでシンプルなスワップで済んでいますが、ふるい下げの方は3者を比較して最小なものを持ち上げるので若干分かりづらいかもしれません。
適当に実行して中身を確認すると
const heap = new MinHeap(); for (let i = 5; i < 15; i++) { heap.insert(i); } console.log(heap.checkHeap());
出力↓
[ 5, 6, 7, 8, 9, 10, 11, 12, 13, 14
]
ちゃんと配置換えが行われているか見るために最小のものを追加してみると
heap.insert(4);
console.log(heap.extract());
出力↓
4
ちゃんと最小の値が取り出せています。
Complexityについてですが、insert、extractどちらでも\(O(log\hspace{2pt}n)\)になります。ヒープの\(n\)段目には\(2^{n-1}\)個要素があって、一段移動すると2の累乗単位で処理が減っていきますので\(log\)になるのはなんとなくご理解いただけると思います。この動画が図など非常にわかりやすいので参考にしてみてください:Big O of Binary Heap
今日は以上です。なんとなく仕組みだけ参考にしつつ、javascriptに置き換えて実装したので、もっと良い実装があればぜひコメントください。
では。
【アルゴリズム(JS実装)】Bubble Sort
どうも!わくばです!
今日から1日1アルゴリズムずつ記事にしていこうかなと思います。
今までなんとなく避けてきたんですがそろそろそうも行かなくなってきたので、、、
普段codewarsというサイトでコーディングの勉強をしているんですが、上級になると結構難しくなるんですよね。codewarsではレベルのことをkyuといって、そのkyuが上がってくるとどうにも素人の発想では回答できない問題も増えてきます。kyuが上がるのに比例して1問にかかる時間が長くなってきまして、そろそろ真面目にアルゴリズムを勉強しておかないと、この先いくら時間があってもてりないのでは、、、というわけです(笑)
勉強の手順としては
- VisuAlgoというサイトから適当にアルゴリズムを一つ選ぶ。
- JavaScriptで実装していみる
といった感じです。
JavaScriptを選んだ理由は、Visualgoのソースコードが載っていなくて、一番馴染みがある言語だからです。
・Bubble Sort
今日勉強したのはBubble Sortです。仕組みは簡単で、「隣接する二項を比較して条件に応じて入れ替える」操作を左から右へ繰り返し行うものです。
「バブル」と呼ばれる所以は、要素がループ処理で徐々に適切な場所に移動し且つばらばらに終了していく感じが、水の底から泡が発生して無秩序に弾ける様子に似ているからだそうです。
今回は配列の要素を昇順に並べ替えるというアルゴリズムを実装してみました。
const bubbleSort = arr => { //分割代入でArrayオブジェクトからlengthを独立させる(必須ではない) const { length } = arr; //swappedがtrueの間だけwhileループを実行 let swapped = false; do { swapped = false; for (let j = 0; j < length - 1; j++) { if ( arr[j]> arr[j+1] ) { //スワップ処理 [ arr[j], arr[j+1] ]=[ arr[j+1], arr[j] ] swapped = true; } } }while(swapped); return arr; }
ループ処理を二重にするというシンプルなアルゴリズムです。Big O notationで計算すると\(O(n)\)が二重なので\(O(n^2)\)の処理になります。
whileではなくforで配列の要素ぶんループする実装もありましたが、whileの方が必要最低限の処理で済むのでスッキリです。
次回はなににしようか。個人的にグラフ理論が楽しみです(笑)
では。
Vimerになりたい!
どうも!わくばです!
前回はアドベントカレンダー用の記事だったのであまり個人的な話はできなかったんですが、実は進級試験中でした(汗)
昨日やっと自己採点も終えて、なんとか進級はできそうってことで一息つけました。2日で400問、計14時間のテストでかなり疲れました(笑)
進級結果は3月なので実際まだわからないんですが、及第点は取れているはずです!
僕の大学では5年から6年になる進級試験で6割取っていれば、一年後の国試は安泰らしいので順調に勉強すれば医者になれるらしいです。ただし順調に勉強すればの話ですが、、、嫌な予感。
さて今回ですが、Vimerになりたい!ということで第一歩を踏み出そうと思います。vimってレベル高い人が使う印象というか、固定観念というか、とにかく敷居が高い印象があったのでずっとVScode使っていました。でも、僕の(勝手に)尊敬しているdvelopperさんが使っていらして、やっぱり頑張って使っていみようと思いました。
クールでかっこいいですよね。この方はInkdropというエディターのSolo developperでゴリゴリのJS使いです。なのでかなり色々と参考にさせていただいています。vimってあんまりデザインには凝っていないのかなと思い込んでいましたが、使用者が多いからかかなりいろんなpluginがあって楽しく使えそうです。
いきなり使い始めるのもハードル高杉やな〜と思ってので、英語ですがまずはTutorialをやってみました。→Interactive Vim tutorial
サイトの雰囲気はこんな感じです。vimのサイトはたくさんありますが、このサイトはインタラクティブで何も知らないう状態でも勉強しやすいです。ただしホントに初歩しかやらないようで、移動とか削除などしかありませんでした。
一通りやってみた感想としては、物足りない!(笑) Tutorialなのでそりゃそう(笑)
ほんとに初歩しか勉強していませんが、それでもVimの便利さは十分感じ取れました。
今度はこのサイトで勉強してみようと思います。→ VimGenius
そしたら実際に自分のVim環境を作ってみてVimライフを満喫しようと思います!
最近寒いですが、お体お気をつけて。あとPCもフリーズしないように気をつけていきましょう。 それでは!
扇形を描画したい初心者の格闘日記〜SVG vs Canvas〜
どうも、わくばです!
ReactNative アドベントカレンダー10日目です!
アドベントカレンダーは初投稿で、拙い内容かもしれないんですが箸休め程度に楽しんでいただければと思います。
プログラミングはまだ始めて半年も経っていない初心者ですが、エンジニアの方々ともっと絡みたいと思って今回アドベントカレンダーに参加させていただきました。
Twitterやってるのでぜひぜひフォローお願いします!!
概要
こんな感じのUIを作るために、SVGとCanvasを用いて扇形の描画を検討してみた記事です。
目次
自己紹介
現在医学部5年です。医療分野においてもITリテラシーが重要だというのはヒシヒシ感じていましたが、明確なインセンティブを持てずなんとなく過ごしておりました。将来的に僻地医療に携わりたいので、絶対に役立つと思ってるんですが。そんな中、半年前に医師からエンジニアへ転職された先生の登壇に感銘を受け一念発起し、本格的に勉強を始めました。当初右も左も分からず、なんとなく医学と親和性の高そうなAIの分野から始めようということでPythonからスタート。G検定合格したあたりから、自分のやりたいことはAIアルゴリズムの作成ではなく現場で使えるツール作りであることに気づき、8月ごろにアプリ開発の勉強を始めました。
現状やっていることは医療とは一切関係ないので、その点ご了承ください。
動機
プログラミングを学ぶに当たり何か自分で作るのが得策だと思い、勉強の一環としてなにか作ろうと思ったのがきっかけです。 扇形の描画方法を模索しようと思ったのは、React Nativeで上の写真のような円形のスケジュールアプリが作りたかったからです。医学の勉強で時間に追われる身としてはスケジューリングアプリが真っ先に思い浮かびました。特に円形のアプリが一日を一目で定量的に把握できるので好みです。
扇形の条件
最初は円グラフを作ればいいんだと思って突き進んでしまったのですが、円グラフだと割合(%)で円を作るので、一個一個の弧に対して開始時間と終了時間が指定できないことに途中で気づきました笑。
泣きながら扇形を単体で描画できるツールを探すんですがこれがなかなか大変、、、、、。
ツールに求める機能は以下の2つ!
- 扇の開始角度と終了角度を極座標的なパラメータを用いて変更できる
- 複数の扇をある中心点から放射状に配置できる
React Nativeではd3.jsやVictory Nativeのような、グラフや表を描画するモジュールはすでに存在しますが、前述の問題点のように一個一個の扇の開度を自由に指定できず、割合(%)で円グラフを作る機能がほとんどなので残念ながら却下。したがってデータビジュアリゼーション系のものではなく、図形描画を自由に作成できるものを探し始めたんですが、初心者ではドキュメントを読むだけでどこまで実装できそうか検討をつけるのは難しく、片っ端から実装していって吟味する事になりました。トホホ。。。。
使用を検討したツール
プロジェクト自体はTypeScriptのExpo(v3.27.6)で作成しています。初心者はTypeScriptのように静的型付けのほうがバグの検索がしやすいかなと思ったためです。
扇形の作成において使用を検討したAPIは以下の2つです。
単に扇形を作るだけならCSSでもできますが、調べる限りCSSなどでは角度を自由に変化させるのが難しそうでした。(できる方法をご存じの方がいましたらぜひ教えていただけるとありがたいです)
SVGを用いた扇
最初にご紹介するのはSvgを用いるものです。一番素直な方法かなと思います。以下にコンポーネントのコードを添付いたします。時計の部分は含まれていないので注意してください。
import React from "react"; import { Dimensions, Platform, PixelRatio } from "react-native"; import { G, Path, Text } from "react-native-svg"; //中心座標を取得 const { width } = Dimensions.get("window"); const outerRadius = width / 2.24; const centerWidth = width / 2; const innerRadius = outerRadius / 2 + 12; const cx = centerWidth; const cy = centerWidth; const fontScale = width / 880; const adjustableScale = (size: number) => { const newSize = size * fontScale; if (Platform.OS === "ios") { return Math.round(PixelRatio.roundToNearestPixel(newSize)); } else { return Math.round(PixelRatio.roundToNearestPixel(newSize)) - 2; } }; type Props = { startDegree: number; finishDegree: number; color: string; children: string; }; //扇形のコンポーネント const FanShape = (props: Props) => { const { startDegree, finishDegree, color, children } = props;
//扇形の開始座標 const outerStartX = cx + outerRadius * Math.sin((startDegree / 180) * Math.PI); const outerStartY = cy - outerRadius * Math.cos((startDegree / 180) * Math.PI); //扇形の終了座標 const outerFinishX = cx + outerRadius * Math.sin((finishDegree / 180) * Math.PI); const outerFinishY = cy - outerRadius * Math.cos((finishDegree / 180) * Math.PI); //扇形の角度が180度を超えているか; const largeArcFlag = finishDegree - startDegree <= 180 ? 0 : 1; //内円 const innerStartX = cx + innerRadius * Math.sin((startDegree / 180) * Math.PI); const innerStartY = cy - innerRadius * Math.cos((startDegree / 180) * Math.PI); //円弧の終わり座標; const innerFinishX = cx + innerRadius * Math.sin((finishDegree / 180) * Math.PI); const innerFinishY = cy - innerRadius * Math.cos((finishDegree / 180) * Math.PI); //弧の中点 const outerMediumX = cx + outerRadius * Math.sin((((startDegree + finishDegree) / 2 + 90) / 180) * Math.PI); const innerMediumX = cx + innerRadius * Math.sin((((startDegree + finishDegree) / 2 + 90) / 180) * Math.PI); const outerMediumY = cy - outerRadius * Math.cos((((startDegree + finishDegree) / 2 + 90) / 180) * Math.PI); const innerMediumY = cy - innerRadius * Math.cos((((startDegree + finishDegree) / 2 + 90) / 180) * Math.PI); //ラベルのポジション const xPositionOfLabel = outerMediumX / 2 + innerMediumX / 2; const yPositionOfLabel = outerMediumY / 2 + innerMediumY / 2; //円弧を描くコマンド return ( <G> <Text fill="black" stroke={color} fontSize={adjustableScale(45)} fontWeight="bold" x={xPositionOfLabel} y={yPositionOfLabel} textAnchor="middle" > {children} </Text> <Path d={`M${innerStartX},${innerStartY} A${innerRadius},${innerRadius} 0 ${largeArcFlag} 1 ${innerFinishX},${innerFinishY} L${outerFinishX},${outerFinishY} A${outerRadius},${outerRadius} 0 ${largeArcFlag} 0 ${outerStartX},${outerStartY} Z`} fill={color} fillOpacity="0.5" stroke="grey" /> </G> ); }; export default FanShape;
propsを変更しリアルタイムで変化するかチェック↓
テンプレートリテラルを用いてPathのコマンドに変数を埋め込んでいます。color, startDegree, finishDegree, childrenをpropsとして親コンポーネントで指定すれば自由に扇形の開度と色調を変更できます。
SVGのPathは小文字のコマンドでは直前の位置からの相対位置を指定でき、大文字のコマンドでは左角を0とした絶対位置を指定できます。上のコードは絶対位置で描画しています。コマンドについてSVGで円グラフを描く | Web活 が非常に参考になりますのでご興味ある方はどうぞ。
メリット
非常に簡潔に書けると感じました。定数の宣言をし、テンプレートリテラルでPathに投げ込むだけです。
デメリット
これは僕自身のコーディング力の問題でもありますが、Canvasと比べて扇の作成部分の位置取りが細かいので若干冗長に感じます。また初心者にとってPathのコマンドが非常に学習コストが高いのではないかと思います。特にarcを描くコマンドは独特です。
Canvasを用いた扇
続いてCanvasを用いた扇形です。まずは扇のコンポーネントのコードです。 同じく時計の部分のコードは含まれません。
import React, { useRef, useEffect } from "react"; import { StyleSheet } from "react-native"; import Canvas from "react-native-canvas"; import { Dimensions, PixelRatio, Platform } from "react-native"; //window情報を取得し半径や中心座標を決める const { width } = Dimensions.get("window"); const outerRadius = width / 2.24; const centerWidth = width / 2; const innerRadius = outerRadius / 2 + 12; const cx = centerWidth; const cy = centerWidth; //テキストサイズを画面に応じて変化させる関数 const fontScale = width / 880; const adjustableScale = (size: number) => { const newSize = size * fontScale; if (Platform.OS === "ios") { return Math.round(PixelRatio.roundToNearestPixel(newSize)); } else { return Math.round(PixelRatio.roundToNearestPixel(newSize)) - 2; } }; type Props = { startDegree: number; finishDegree: number; color: string; label: string; }; const FanShapeByCanvas = (props: Props) => { const { startDegree, finishDegree, color, label } = props; const canvasRef = useRef(null); const getContext = (): CanvasRenderingContext2D => { const canvas: any = canvasRef.current; //キャンバスを画面サイズに合わせて正方形にとる canvas.width = width; canvas.height = width; return canvas.getContext("2d"); }; useEffect( () => { //propsの変化時にラベルの位置を取り直す let xPositionOfLabel = cx + (Math.cos(((startDegree + finishDegree) / 2 / 180) * Math.PI) * (innerRadius + outerRadius)) / 2; let yPositionOfLabel = cy + (Math.sin(((startDegree + finishDegree) / 2 / 180) * Math.PI) * (innerRadius + outerRadius)) / 2; //context作成 const ctx: CanvasRenderingContext2D = getContext(); ctx.beginPath(); //内円 ctx.arc( cx, cy, innerRadius, (startDegree / 180) * Math.PI, (finishDegree / 180) * Math.PI, false ); //外円 ctx.arc( cx, cy, outerRadius, (finishDegree / 180) * Math.PI, (startDegree / 180) * Math.PI, true ); ctx.closePath(); ctx.strokeStyle = "#0000ff"; ctx.fillStyle = color; //color propsを受け取る ctx.stroke(); ctx.fill(); ctx.font = `bold ${adjustableScale(45)}px Arial`; //画面のサイズ変化に応じてテキストサイズを変化させるadjustableScale関数を上で定義しています ctx.fillStyle = "black"; ctx.fillText(label, xPositionOfLabel - 25, yPositionOfLabel, 300); }, [startDegree, finishDegree, color, label] //canvasは副作用で図を描くので角度や色の変更があった際は再描画が必要。そのためuseEffectのトリガーをpropsにする ); return <Canvas style={styles.canvas} ref={canvasRef} />; }; const styles = StyleSheet.create({ canvas: { position: "absolute", opacity: 0.5, }, }); export default FanShapeByCanvas;
メリット
円弧の作成部分のコードだけ見るとCanvasのほうがSVGより簡略に記述できるので使いやすいとは思います。arcの設計がSVGほどクセがなくて、シンプルに極座標なので比較的直感的です。
デメリット
致命的ですがExpo非推奨になっています笑
またCanvas属性をTypeScriptで扱おうとするとエラーが吐かれるので、そこのやりくりがめんどくさいです。多分普通のJavaScriptだといけると思います。TypeScriptはまだ学習中なのでだいぶ苦戦しました(汗) 上のコードでは
const getContext = (): CanvasRenderingContext2D => { const canvas: any = canvasRef.current; canvas.width = 500; canvas.height = 500; return canvas.getContext('2d'); };
においてCanvasRenderingContext2Dとconst canvas: any = canvasRef.current;
の部分で明示的に型を宣言しています。
CanvasをReactで用いる場合useRefでDOMの参照を維持しておかないといけないのでその点が初心者には難解だと感じました。
またちゃんと速度を図らないと正確なことは言えませんが、Canvasのほうが若干遅いかなと感じます。一般的にCanvasのほうが早いらしいんですが、僕の書いたコードですとpropsに変更が加わるたびにuseEffectが走って再レンダリングされるのでその分遅くなるんだと思います。ここはもう少し勉強が必要だと感じました。
まとめ
SVGでもCanvasでもほぼ同じ図形が描画でき、両者とも角度の変更など柔軟な対応ができそうでしたが、今回はSVGに軍配が上がりそうです。というのも、react-native-canvasは公式で非推奨ですし、Githubのissuesなどもだいぶ放置されていて対応が追いついていない感じがありました。
またCanvasは軽量で、アニメーションなどを描画する際に効果を発揮するそうなので、今回は非推奨を無視してまで使うほどメリットはないと思われます。
SVGならPathさえ習得すればおそらく好きなように図形が作れますし、今後のことを考えればSVGに慣れておくのが賢明と判断しました。
誤っている点のご指摘やご意見などございましたら遠慮なくコメント下さい。メッチャクチャ嬉しいです!
エンジニアの方々ともっと絡みたいので遠慮は無用です!ぜひTwitterの方もフォローお願いします!
つくりたいアプリがたくさんあるので引き続きReact Nativeの学習頑張っていこうと思います。
では。
Define-and-RunとDefine-by-Run
わくばです!
最近は薬理学研究室の手伝いでTensorFlowを勉強しているんですが、使っていてすごく違和感があるというか、使いづらいんですよね。
なんでかなと調べてみたら「Define-and-Run」という設計思想がどうやら関わっていそうなので今回記事にしてみました。
Define-and-Runを考えるにあたってよく引き合いにだされるのがDefine-by-Runです。それぞれ日本語で訳すと前者は「定義して実行する」、後者は「実行ごとに定義する」と言った感じでしょうか。
Define-and-Runというのはまず予め静的なネットワークを記述した後、実際にデータを用いて実行する形式です。ここで予め記述するネットワークのことを「計算グラフ」というそうです。この「計算グラフ」が一連の流れ(フロー)を示していて、そこへテンソルを流し込んで学習を実行するというのが、おそらく「TensorFlow」の由来なのでしょう。コンパイラ言語や静的言語の使用感に近く、宣言的な感触でした。
Define-by-Runは実行を行いながら動的にネットワークの構築を行っていくものです。代表はPyTorchでしょうか。インタプリタ言語や動的言語のような印象です。Define-and-Runでは"ネットワークのロジック"をデータとして保持して学習を実行するのに対して、Define-by-Runは"計算履歴"をデータとして保持することで、フレキシブルにロジックを組みながら実行を行えるようです。
通りで違和感があるわけだ。僕は基本的にPythonやJavascriptなどの動的なインタプリタ言語しか触って来なかったのですから。TensorFlowではPythonのようにロジックを組みながら渡ってくる値を確認することができないので霧の中を進んでいるような感覚でした。慣れもあるのかもしれませんが、TensorFlowとPyTorchで好みが分かれる理由もうなずけます。
ただし自然言語処理ではDefine-and-Runのほうが向いているなどの意見もあって一概に好みだけでは決められないようです。TensorFlowもDefine-by-Runに対応できるものが出てきていますし、まだまだ勉強が必要ですね。
引き続き勉強頑張りたいと思います。
では。
<#5 React Native>〜useStateを学ぶ〜
わくばです!
今回はuseStateについて説明してみます。useStateというのは関数コンポーネントの状態を管理する機能を言います。状態ってなんぞやと思うかもしれませんが、これは具体的にコードで示した方がわかりやすいので、単純な機能を作ってみました。
useStateの例
まず以下にReactNativeのソースコードを添付します。水の温度をState(状態)として管理し、それによって水の物性変化を管理しています。
基本的な文法としてはconst [xxxx,setXxxx]=useState[初期値]
という形を取ります。xxxxの部分は自分で好きなように名前を決められます。ただしsetのあとは大文字です。以下のプログラムではmatter( temperatureとかにしとけばよかった.......)という名前でstateを扱い、初期値は-40としています。
緑の枠の中で、state(温度)によって水がどんな物性になるかを場合分けし、赤の枠でボタンを描画しています。
以下のリンクでこのプログラムが実際にどんな挙動を示すのか見れますのでぜひリンクに飛んで見てみてください。ボタンをクリックすると温度が変化しそれによって水の物性も変化していきます。
なんとなくおわかりいただけましたか。このように関数コンポーネントにstateを保持させ、setStateでstateを更新することで、関数コンポーネント挙動をコントロールすることができます。ボタンをクリックするごとに30℃ずつ温度が上昇していますよね。ボタンを押すことでonPressのsetStateが機能し、state の値を+30しています。
このようにstateを管理することでインタラクティブにUIを変化させることができるんです。
余談 : Hooks
このuseStateというのは本来「use」はついていなくて、stateというクラスコンポーネントのプロパティとして用いられていました。確かに関数というは処理が済んだらそれで終わりですから、値をずっと保持して随時更新するというのはクラスの方が得意ですよね。しかしクラスコンポーネントはコードが大きくなるとものすごく複雑になるらしく保守性などが非常に低下するそうです。そこで開発されたのがこのReact Hooksという機能(API)で、これによって関数コンポーネントでも状態の保持とその更新が可能になりました。僕は大きなコードを扱ったことがないので実感はありませんが、React公式は完全に関数コンポーネントによる記述を推進していくようです。
「use~」がついたHooksは他にもたくさんあるので少しずつ自分の口で説明できるように詳解していこうと思います
では。