240 lines
26 KiB
Text
240 lines
26 KiB
Text
## 3/16
|
||
Rubyの真偽値はfalseまたはnil以外はすべてtrue:Ruby固有の特徴
|
||
if文の戻り値を変数に代入できる:greeting = if country == 'japan' elsif...のように書ける
|
||
メソッド名はスネークケースで書く:hello_world のような形式
|
||
%記法でエスケープが不要になる:%!unkoburi!のように囲える
|
||
ヒアドキュメント:<<-識別子 テキスト 識別子 の形式で複数行の文章を格納できる。式展開も使える。 <<-を途中で使うとインデントされる。引数として渡すこともできる。ヒアドキュメントを二つ使うと配列を作れる(これすごい便利じゃないか?)
|
||
配列の結合:[10,20,30].join で全部くっつく
|
||
unless:ifの逆 でもこれ逆に頭がこんがらがりそう
|
||
==true , ==false は冗長なので使わない:それはそうだ
|
||
case文:絶対に使うべき局面でもなぜか忘却してif文を使ってしまう message = case xxx when 'yyy' end のように使う
|
||
三項演算子:絶対に使うべき局面なのになぜか忘却して以下略 式 ? 真だった場合の処理 : 偽だった場合の処理 のように使う
|
||
メソッドにデフォルト値の引数を付ける:def greet(country = 'japan')などで引数なしでjapanが引数として扱われる。(x, y=x)のようにするとyが無指定の場合にxと同じ値として扱われる
|
||
!で終わるメソッドは破壊的メソッドだが、ついていないからといって破壊的ではないとは限らない:concatは付いていないが破壊的
|
||
エンドレスメソッド定義 endがいらない:def greet = 'hello!'
|
||
lengthとsizeはどちらも”まったく”同じメソッド:なにか違うはずだろと思っていたのに……
|
||
標準ライブラリと組み込みライブラリはイコールではない:同じだと思ってた
|
||
putsとprintの差は改行の有無だが、pはオブジェクトそのものがメソッドの戻り値になる(p 123の後に123を参照すると123が戻ってくる、putsの場合はnil)、ppはpよりも整形してきれいに出力してくれる。pはオブジェクトをStringではなくinspectに変換している
|
||
配列で元の大きい添字を指定すると間の値はnilで埋められる:a=[1,2,3] a[4]=50などとすると[1,2,3,nil,50]となる
|
||
配列はa << 2 などで最後に要素を追加できる:コードを見た覚えがある割には自分で使ったことはなかった
|
||
削除はdelete.at(n)で行う。delete(n)だとnに一致する要素がまとめて削除される
|
||
divmodというメソッドは商と余りを配列で返す 14.divmod(3)は[4,2]になる delete_ifメソッドを使うとdo |x|で条件を別に記述できる
|
||
Rubyプログラマはfor文を使わない:確かに例文でほとんど出てこないので気にかかっていた。たまたまかと思いきや本当に使わないのか
|
||
配列の要素を順番に取り出す作業はeachメソッド、要素をどう扱うかはブロック(ブロックの中身に書くコード)の役割:ごっちゃになっていたのでようやく納得した
|
||
変数名の重複により他のものが参照できなくなることをシャドーイングという
|
||
do endの代わりに{}でブロックをくくることもできる:僕が最初にうまく理解できなかった理由はこれだな
|
||
.mapを使うとブロックの戻り値が配列の要素となる新しい配列が作成される:new = numbers.map { |n| n * 10}で配列numbersを10倍にした新しい配列が作れる
|
||
112ページまで
|
||
|
||
## 3/17
|
||
numbers.select {|n| n.even?} : このようにselectメソッドを使うと条件を満たした配列のみを作成できる。この例では偶数のみの配列が生成される
|
||
numbers.find : findメソッドは戻り値が真になった最初の値のみを返す
|
||
numbers.sum : 名前通り配列の要素を合計できる。文字列でも使える('')単純な連結ならjoinメソッドの方が楽だがsumは文字列の加工に優れる
|
||
&:メソッド名でシンプルに書ける:ブロックパラメータが1つ、ブロックの中のメソッドに引数がない、メソッドを1回呼び出す以外の処理がない、といった条件をすべて満たすと [1,2,3,4,5,6].select{|n| n.odd?}を[1,2,3,4,5,6,].select{&:odd?}のように簡潔に書ける。演算子を使っている場合は不可能
|
||
(1..5)←これはRangeという名前のオブジェクトだった!:ただのそういう感じの簡略記法かと思っていた。点が三つだと5は含まれなくなる
|
||
(1..5).to_aで値が連続する配列を作ることができる:これ””””答え””””だ。こういうものがあるといいと思ってた
|
||
RBG変換器の実装を通じて:これたぶんもっと短くなる方法ありそうだな……と思っていたらすぐ下でsumを使った記法が書かれていた。ぜひものにしたい
|
||
123ページまで
|
||
|
||
## 3/19
|
||
to_intsメソッドのリファクタリングを通じて:空の配列を用意して、ほかの配列をループ処理した結果を空の配列に詰め込んでいくような処理の大半はmapメソッドに置き換えることができる……らしい
|
||
上級編に書かれていた多重代入➜scanメソッド➜mapメソッドを呼ぶからの&シンボ➜エンドレスメソッドの流れはカードバトのフィニッシのコンボみがある
|
||
a.values_at(0,2,4)この形式で取得したい要素を複数指定できる。
|
||
a[a.size - 1]この形式で最後の要素を取得できる
|
||
a[-1]これでもいける
|
||
a.lastこれもいける
|
||
a[開始地点,範囲] = 置き換える数字 で要素を置き換えられる
|
||
a.concat(b)でaを破壊的にbと結合する(bは破壊されない)
|
||
e, *f = 100,200,300このような書き方をするとはみ出した数字を含めた配列がfに代入される
|
||
e, * = 100,200,300 この場合は100のみがeに格納されて他は無視される
|
||
e, = 100,200,300 これも同じ
|
||
a, *b, c, d = 1,2,3,4,5 この場合は間に挟まれた残りの要素がbに入る(2,3)
|
||
配列を配列にpushで代入する場合、スプラット演算子をつけなければ多重配列になる
|
||
上記の特性を活かしてwhen節で配列を複数の条件として展開できる
|
||
上記の特性を活かして簡潔な形で配列を連結させることもできる[-1, 0, *a, 4, 5]
|
||
%w記法:これはフォーマッタが教えてくれた。%W大文字を使うとタブ文字なども使える
|
||
'Ruby'.chars この形式で文字を一文字ずつ分解して配列にできる
|
||
'Ruby,Java,Python'.split(',') 文字列から配列を作る
|
||
a = Array.new(5, 0) 配列に初期値を設定して作る方法。この例では5つの0が配列に入る
|
||
a = Array.new(10) {|n| n % 3 + 1} ブロックを使った初期値の設定例。引数の数だけ呼ばれてブロックの中身が要素の添字になる
|
||
第2引数デフォルト値を指定するとすべての配列が同じ文字列オブジェクトを参照するため、破壊的メソッドですべてが置き換わる
|
||
ミュータブルとイミュータブル:イミュータブルなオブジェクトは破壊的変更の影響を受けない。主に数値、シンボル、真偽値、nil、Rangeはイミュータブル
|
||
freezeメソッドを使うとイミュータブルなオブジェクトにすることができる
|
||
with_indexはeach以外でも使えるmapやdelete_ifなど : Enumeratorクラスに属するものはwith_indexを呼び出せる
|
||
配列の要素分だけブロックパラメータを用意すると各要素の値が別々の変数に格納される
|
||
ブロックパラメータを丸括弧で囲うとwith_indexなどで他のブロックが必要になる場合でも上記の挙動を実現できる
|
||
番号指定パラメータ:_1 このような形式でブロックに格納する変数を指定できる
|
||
do..endの代わりに使う{}は結合度が高い:異なった解釈をする場合があるのでブロックに丸括弧が必須
|
||
しかしながらdo..endと{}は基本的には同じなのでendの後ろにドットをつけて.joinなどをつけても機能する
|
||
151ページまで
|
||
|
||
## 3/20
|
||
始点を持たない範囲オブジェクト:(10..) これで10以上を表す。そんなことできるんだ。逆も可能 numbers[2..]などで3番目以降の要素を取得する
|
||
(nil..nil)で全範囲オブジェクトを作成可能
|
||
timesメソッド 5.times {|n| sum += n} などのように使う:知っているはずなのにこれまで使う機会がなかった
|
||
1ずつ増減させながら処理したい場合はupto/downtoメソッドが便利: 10upto(14) {|n| a << n} のような形式で1ずつ14まで増える配列が作れる
|
||
一定の数を増減させる場合はstepメソッドを使う : 1.step(10,2) {|n| a << n} これで2ずつ10未満まで増える
|
||
while文 while 真である条件 処理 endで書く:Rubyだと影が薄い気がする。逆バージョンのuntil文もある
|
||
for文 使わないけど一応ある: for n in numbers sum+= n endのように書く eachとの違いはローカル変数がforの外でも使えるところ
|
||
意図的に無限ループを作る:loop do 処理 end
|
||
throwとcatch: これRubyでもあるんだ……と思っていたが他の言語のthrowとは意味が違っていた。breakの強化バージョンとして使う。catch :tag do ループ処理1 ループ処理2 throw :tag endで内側から一気に外側まで抜ける
|
||
breakとreturnの違い:breakは繰り返し処理からの脱出だが、returnはメソッドからの脱出になる
|
||
redo 繰り返し処理をやり直す:RPGの質問みたいなことができる countで制限回数を指定できる
|
||
エイリアスメソッドがたくさんある理由:他の言語との整合性、直感性、あとからもっといい名前をつけた、英語との一体感
|
||
ここから第五章
|
||
ハッシュとはキーと値の組み合わせでデータを管理するオブジェクト:他の言語では連想配列やディクショナリ、マップと呼ばれている
|
||
{ キー1 => 値1, キー2 => 値2}
|
||
ハッシュリテラルとブロックの見分け方:ロケットでキーと値が区切られている(ハッシュ)、ブロックパラメータがある(ブロック)、処理が書かれている(ブロック)
|
||
ハッシュの利点:大量の値が格納されていても指定したキーに対応する値を高速に取り出せる
|
||
ハッシュの比較:並び順が違っていてもキーと値の対応が同じであればtrue
|
||
ハッシュのeachメソッド:ブロックパラメータを1つにするとキーと値が配列に順番に格納される
|
||
シンボル:他の言語ではあまり見られない特徴らしい。コロンの後に続いて文字列を書くとシンボルリテラルを作ることができる。
|
||
シンボルの利点:文字列をよく似ているが整数として扱われるので文字列よりも高速に処理できる
|
||
シンボルの利点2:同じシンボルは同じオブジェクトとして扱われる。なので大量の同じシンボルは文字列よりもメモリの効率に優れる。
|
||
シンボルの利点3:シンボルはイミュータブルなオブジェクトなので破壊的変更ができない
|
||
シンボルの用途:ハッシュのキーに用いる。文字列よりも高速に値を取り出せる。キーは文字列である必要性がないのでよく使われる。
|
||
シンボルの用途2:可読性の向上。特定の数値で任意の状態を表すと計算効率はいいが可読性が悪い。シンボルを使うと効率をあまり犠牲にせずに可読性を高められる
|
||
シンボルの利用でロケットを省略する:{japan => 'yen'}このように書くものを{japan: 'yen'}こうして書ける。圧倒的に記述量が減る。
|
||
両方ともシンボルにする: {japan: :yen} かなり使われているらしい
|
||
ハッシュのキーはそれぞれ別のデータ型でも問題ないが推奨はされない。一方で値の方はよく混在する(例:人物目録を作る際は名前が文字列、歳が数値、人間関係が配列、電話番号がハッシュなどになったりする)
|
||
キーワード引数:buy_burger(menu, drink: true, potato: true) end シンボルと似たような形式で可読性の高い引数を設定できる
|
||
アスタリスク二個でハッシュの変数を指定すると中身をキーワード引数として利用できる
|
||
メソッド定義側のキーボード引数はシンボルっぽいだけであってシンボルではない:しかし呼び出す側はれっきとしたシンボルである(紛らわしすぎる)
|
||
ハッシュで使われるメソッド色々:keys(キーを配列として返す)、values(ハッシュの値を配列として返す)、has_key?(読んで字のごとく)
|
||
定義されていないキーワードを同時に受け取るにはアスタリスク2個の引数を最後に用意する
|
||
189ページまで
|
||
|
||
## 3/21
|
||
最後の引数がハッシュであればハッシュリテラルの{}を省略できる:楽だが運用に注意を要しそう
|
||
メソッドの第1引数にハッシュを渡そうとする場合は必ず()を付けてメソッドを呼び出す必要がある
|
||
ハッシュをto_aメソッドで配列に変換すると二次元配列になって返る
|
||
逆もできるがキーと値の組み合わせが配列に入った二次元配列になっている必要がある
|
||
h = Hash.new { 'hello' } : Hash.newとブロックを組み合わせてデフォルト値を返すことで破壊的変更の影響を避けられる
|
||
...引数を使うと通常の引数とキーワード引数をまとめて委譲できる:先頭のいくつかの引数を取り出すこともできる
|
||
%sでシンボルを作成できる。%iでシンボルの配列を作成できる
|
||
条件分岐内で直接変数に代入するテクニック(値が取得できれば真、できなければ偽) if currency = find_currencty(country) 処理 end
|
||
&.演算子を使ってメソッドを呼び出すとnilの場合はnil、nilでない場合はその結果が返ってくる
|
||
nilガード: limit ||= 10の形式でnilまたはfalseであれば10を代入するというような機能を持つ。nil以外の値を入れておきたいが書き換えられたくない場合に使う
|
||
trueならtrue、falseならfalseというような条件分岐を書く場合はエクスクラメーションマーク2個を使った真偽値の型変換が有効に使える
|
||
ここから第6章
|
||
正規表現ってマジで苦手だ。これまでもなるべく避けて生きてきたがついにTAMESAREの時が来たらしい
|
||
「書籍を読んでいると思ったらWebページに誘導された……なにを言っているのかわからねえと思うが」になった
|
||
\d は1文字の半角数字を表す
|
||
文字の個数を限定するときは {n,m} や {n} というメタ文字を使う(量指定子)
|
||
{n,m} は「直前の文字が n 個以上、m 個以下」の意味を表す
|
||
また、 {n} とすれば「ちょうど n 文字」の意味になる
|
||
「AまたはBのいずれか1文字」表す場合は [AB] と書く
|
||
[a-z] と書くと、「aまたはbまたはcまたは・・・yまたはz」の意味になる。
|
||
ただし、[-az] や [az-] のように、ハイフンが [ ] の最初、または最後に置かれると「ハイフン1文字」の意味に変わる。
|
||
なので [-az] や [az-] は「aまたはzまたはハイフンのいずれか1文字」の意味になる。
|
||
209ページまで
|
||
|
||
## 3/22
|
||
文字がない場合も指定する時は?を使う。これも量指定子の一つ。2文字以上の文字列に対しても使える。(ABC)?でABCがある、またはなし、を意味する
|
||
ドットで「任意の一文字」を表すことができる
|
||
直前の文字が1文字以上、を表すには+を使う。これも量指定子。0文字以上の場合は*を使う
|
||
キャプチャ:丸括弧で文字列を囲うと連番が付けられる $1,$2というように指定する
|
||
キャプチャの除外:(:? )の形式で:?を先頭に加える
|
||
\w で英単語を構成する文字 = [a-zA-Z0-9_]を意味する(RubyとJavaScript)
|
||
A以外の任意の文字:[^A]で表せる
|
||
最小量指定子:*?や+?にすると最長ではなく最短のマッチを返す
|
||
素の^は行頭を表すメタ文字:^ + で行頭からスペースが1文字以上続く、という意味になる
|
||
素の$は行末を表すメタ文字:^ +$で行頭から行末までスペースが1文字以上続くという意味になる
|
||
| でOR条件
|
||
[AB^] このように後方に^をつけると「AでもBでもない文字1文字」という意味に変わる
|
||
/b 単語の境界を表す:\bt\bというような書き方でtメソッドを抜き出すようなテクニックがある
|
||
(?<=abc) : この書き方で文字列の直後の位置にマッチする。肯定の後読みと言う
|
||
(?=abc):この書き方で文字列の直前の位置にマッチする。肯定の先読みと言う
|
||
(?<!abc) : 否定の後読み
|
||
(?!abc) : 否定の先読み
|
||
Web記事読了
|
||
|
||
## 3/23
|
||
正規表現のマッチ: =~ で比較するとマッチした場合にtrueが返る
|
||
Ruby上でキャプチャされた文字列を抜き出すmatchメソッド:m = /(\d+)年(\d+)月(\d+)日/.match(text) m[1]などの形式で取得できる
|
||
(?<name>)でキャプチャに名前を付けられる m[:name] シンボルで指定する。文字列でもできる
|
||
左辺に正規表現リテラルを置いて=~演算子を使うとキャプチャの名前がローカル変数に割り当てられる:ようは#{year}で(?<year>)の中身が返る
|
||
↑変数に一旦入れると機能しない
|
||
組み込み変数:$~でオブジェクト全体、$&でマッチした部分全体、$1などでキャプチャを取得できる
|
||
scanメソッド:引数で渡した正規表現にマッチする部分を配列に入れて返す '123 456 789'.scan(/\d+/)で["123","456","789"]が返る
|
||
↑正規表現に()があるとキャプチャされた部分が二次元配列の内側に入って返る。グループ化はしたいがキャプチャはしたくない(全体を取得したい)場合は(?:)で除外を行う
|
||
[]に正規表現を渡すと、文字列から正規表現にマッチした部分を抜き出す: text[/\d{3}-\d{4}/]で"123-4567"
|
||
↑マッチする部分が複数ある場合は最初にマッチした文字列のみ返る
|
||
キャプチャを使うと第2引数で何番目のキャプチャを取得するか指定できる: tex[/(\d+)年(\d+)月(\d+)日/, 3]で"17"が返る
|
||
キャプチャに名前を付けていると第2引数も名前で指定できる
|
||
sliceメソッドは[]のエイリアスメソッドでつまり同じ効果を持つ:slice!で破壊的に取り除かれる
|
||
splitメソッドに正規表現を渡すとマッチした文字列を区切り文字にして分解して配列として返す:text.split(/,|-/) でカンマまたはハイフンを区切り文字にするの意味になる
|
||
gsubメソッド:第1引数の正規表現にマッチした文字列を第2引数の文字列で置き換える text.gsub(',', ':')で123,456が123−456になる。ハッシュに渡すこともできる。hash = {',' => ':'} text.gsub(/,|-/, hash)で123,456-789が123:456/789になる
|
||
第2引数を渡す代わりにブロックの戻り値で置き換える文字列を指定できる:text.gsub(/,|-/){ |matched| matched == ',' ? ':' : '/' }
|
||
gsubとキャプチャを組み合わせて文字列を置換する:text.gsub(/(\d+)年(\d+)月(\d+)日/, '\1-\2-\3') このように連番指定は\1、\2の形で行う
|
||
↑ただし文字列をシングルクオートで囲うかダブルクオートで囲うかで書き方がかなり変わる。後者の場合はバックスラッシュが2つ必要なのでかなり面倒。そこでブロックを使うやり方も考慮に入る。: text.gsub(/\(d+)年(\d+)月(\d+)日/) do "#{$1}-#{$2}-#{$3}" end
|
||
キャプチャが文字列の場合は\k<name>のように先頭に\kを加える
|
||
なお、公式リファレンスではブロックを使う方法が推奨されている
|
||
プログラムの作成を通じて:これはいいものだ
|
||
Regexp.new : 正規表現オブジェクトを作成する
|
||
%r!文字!: スラッシュをエスケープせずに正規表現を作る
|
||
%r{文字}:こっちのほうがわかりやすそう
|
||
正規表現オプション: /iは大文字小文字を区別しない /mはドットを改行文字にもマッチさせる /xオプションは改行やスペースが無視されコメントが書ける
|
||
Ragexp.last_match : 正規表現のマッチやキャプチャを取得できる。フォーマッタにかけるとこれに置き換わる
|
||
match? : マッチするとtrue、しなければfalseを返すのは他と同じだが内容を書き換えないので高速に処理できる
|
||
231ページまで
|
||
|
||
## 3/24
|
||
クラスの利点:堅牢なプログラムを作ることができる
|
||
↑nilではなくちゃんとエラーが返ってきたり、内容の変更を防止できる
|
||
クラス:すべてのオブジェクトが属するもの。クラスが同じであれば使えるメソッドも同じ。
|
||
オブジェクト、インスタンス、レシーバ:クラスをもとにして作られたデータの塊がオブジェクト、場合によってはインスタンスと呼ぶこともある。メソッドとの関係を説明する場合においてはレシーバと呼ぶこともある。
|
||
↑「2行目でUserオブジェクトのfirst_nameメソッドを呼び出しています。ここでのfirst_nameメソッドのレシーバはuserです」というように使う。
|
||
↑つまり「メソッドを呼び出された側」というニュアンスを強調する時にレシーバと呼ぶ
|
||
メソッド:オブジェクトが持つ動作をメソッドと呼ぶ。他のプログラミング言語では関数やサブルーチンと呼ばれている。
|
||
属性(プロパティ、アトリビュート):オブジェクトから取得したり設定できる値のことを属性と呼ぶ。
|
||
クラス名は必ず大文字で始める
|
||
User.new : オブジェクトを作成する。この際にinitializeメソッドが呼ばれる。他の言語ではコンストラクタと呼ばれている。
|
||
インスタンスメソッドの定義:クラス構文の内部でメソッドを定義するとインスタンスメソッドになる。Class.methodの形式で呼び出せるようになる
|
||
インスタンス変数:同じオブジェクトの内部で共有される変数。頭に@がつく。
|
||
ローカル変数のスコープ:その変数が宣言された位置から自身が宣言されたメソッドまたはブロックの終わりまで。ローカル変数は参照する前に必ず値を代入して作成しなければならない。
|
||
↑インスタンス変数は作成する前にいきなり参照してもエラーにならずnilが返る
|
||
インスタンス変数はクラスの外部から参照できず、したい場合は参照用のメソッドを作る必要がある
|
||
イコールで終わるメソッド名:name=(value)のような形式でインスタンス変数を外部から変更するメソッドを作る時などに使われる。これをセッターメソッドと呼ぶ。アクセサメソッドとも呼ぶ
|
||
単純にインスタンス変数の内容を外部から読み書きするぶんにはattr_accessorが使える:これ「ゼロわか」でやったな。フォーマッタにかけるとこれに書き換わる。読み取り専用のattr_readerメソッド、書き込み専用のattr_writerなどもある。
|
||
クラスメソッドの定義:頭にself.のついたメソッドはクラスメソッドになる。そのクラスに関連しているがインスタンスに含まれるデータは使わないメソッドを定義する場合に使われる。class << self 処理 endとしてもよい
|
||
メソッドの表記:インスタンスメソッドを表す場合にクラス名#メソッド名と書くことがある。クラスメソッドの場合はクラス名.メソッド名、またはクラス名::メソッド名と書く
|
||
255ページから
|
||
|
||
## 3/25
|
||
例題を通じて: かなりメソッドが使われている印象を受けた
|
||
Minitestではsetupメソッドを定義するとテストメソッドの実行前に毎回setupメソッドが呼び出される
|
||
メモ化:インスタンス変数とnilガードを使ってデータを保持できる
|
||
def twitter_data
|
||
@twitter_data ||= begin
|
||
処理
|
||
end
|
||
end
|
||
こうするとtwitter_dataメソッドを呼び出した時だけAPIからデータを取得する処理が実行される。2回目以降の呼び出しでは@twitter_dataに保存された値が返却されるだけなのでプログラムのパフォーマンスが向上する
|
||
遅延処理化:メモ化と同様にインスタンス変数とnilガードを使う。
|
||
class Foo
|
||
def bar
|
||
@bar ||= なんかむっちゃ重い処理
|
||
end
|
||
end
|
||
barメソッドを呼び出さない限り重い初期化処理が走らないため、プログラムのパフォーマンスを最適化しやすくなる。
|
||
selfキーワード:インスタンス自身を表すキーワード。Javaにおけるthisキーワードとほぼ同じ。自動的に省略されているが明示的に呼び出すこともできる
|
||
セッターメソッドを呼び出す際にはselfを付ける:さもなければローカル変数の呼び出しと解釈される
|
||
継承:is-aの関係で理解する。「DVD is a product」は違和感がないのでProductをスーパークラスとしてDVDをサブクラスに置いても適切な継承関係である
|
||
標準ライブラリの継承関係:BasicObjectクラスが頂点で次がObjectクラス、次いでString、Numeric、Array、Hashが君臨している。
|
||
なにひとつ定義していないクラスでもObjectクラスを自動的に継承しているため関連するメソッドを呼び出せる
|
||
270ページから
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|