[Scala]無限ループより再帰を使うのがScalaらしいコードらしい
Scalaで無限ループを書くとエラー
投資商品等で、スタートの金額、利率、目標金額を与えて、何年後に到達するかを計算する関数をループで書きたいとします
※本当は対数計算のほうが筋が良いと思います
Javaの感覚で以下のように書くと、コンパイル時にエラーが出ます
def calc(start: Int, rate: Double, goal: Int): Int = { var years = 0 var current: Double = start while(true) { if (current >= goal) { return years } years += 1 current = current * rate } } > Found: Unit Required: Int while(true) {
これは、Int
を返さないといけないところ、scalaは最後の文を自動的に戻り値として認識するゆえに、最後の文であるwhile
が値を返さないためUnit
と認識されてしまっているために起こるエラーのようです
無限ループを成立させるには
到達することはないものの、最後に例外を投げてあげると成立します。
def calc(start: Int, rate: Double, goal: Int): Int = { var years = 0 var current: Double = start while(true) { if (current >= goal) { return years } years += 1 current = current * rate } // この文を追加するとコンパイルが通る throw new RuntimeException("ここには来ない"); }
積極的に再帰を使う
本題なのですが、他言語を普段書かれている方だと、再帰はStackOverflowを起こす可能性があることから、普段は再帰よりもループを選ばれると思います
ところが、Scalaでは、 末尾再帰の関数であれば、コンパイル時にループに書き換え、最適化をしてくれます
末尾再帰とは、 自身のシンプルな再帰呼び出しが、最後のステップになっているパターン のことです
例えば、これは末尾再帰です
def foo(): Int = {
foo()
}
これは、末尾再帰ではありません
自身の呼び出しに+ 1
と、余計なことをしてしまっているためです
def bar(): Int = { bar() + 1 }
そして、@tailrec
アノテーションを付与することで、末尾再帰ではない、最適化がかからない関数に関しては、エラーを発生させることが可能です
まとめると、今回のケースだと、以下のように書き換えが可能です
import scala.annotation.tailrec @tailrec def calc(current: Double, rate: Double, goal: Int, years: Int): Int = { if (current >= goal) { years } else { calc(current * rate, rate, goal, years + 1) } }
参考
Scala while(true) type mismatch? Infinite loop in scala? - Stack Overflow
[Scala]早期returnをしない方がScalaらしいコードらしい
タイトルの件、Javaだと「もし~~なら~~、それ以外なら~~を返す」という事をしたいときに、早期returnにて実装することがあります。
public String hoge(boolean isFoo) { if (isFoo) { return "FOO!"; } return "BAR!"; }
Scalaでも同じように実装できますが
def hoge(isFoo: Boolean): String = { if (isFoo) { return "FOO!" } "BAR!" }
Scalaのドキュメントにある通り、returnは必要なければ好まれないようです。
メソッド本体にある最後の式はメソッドの戻り値になります。(Scalaにはreturnキーワードはありますが、めったに使われません。)
言語特性を活かして、以下のように書けます。
def hoge(isFoo: Boolean): String = { if (isFoo) { "FOO!" } else { "BAR!" } }
Scalaの場合if
が「式」なので、関数の最後にあるif
が丸ごと戻り値となるためです。
Javaの三項演算子と同じ考え方ですね。
参考
[AWS Summit Tokyo 2023]ノベルティをもらい過ぎてしまったので紹介と反省
- NHN テコラス株式会社
- アイレット株式会社
- Dynatrace合同会社
- 日本テラデータ株式会社(かばんフック)
- dataris(ARアドバンストテクノロジ株式会社)
- New Relic株式会社(Tシャツ 抽選)
- レッドハッド株式会社
- CircleCI Japan合同会社
- TIS株式会社(蒸気でアイマスク)
- C-Chorus(NHN テコラス株式会社/PCクリーナー)
- サイオステクノロジー株式会社
- Mackerel(株式会社はてな)
- AWS公式
- KDDI株式会社
- トレンドマイクロ株式会社
- どこかのチロルチョコ(すみません……)
本日、AWS Summit Tokyoに行ってきました。
質の高いセッションと、それから得られるモチベーションとともに、営業スマイルに負けていっぱいお話を聞いて、たくさんのノベルティをもらってきてしまいました……。
スカスカで行ったリュックサックが、帰るころにはパンパンでした。
折角いろいろ頂いたので、宣伝をさせて頂ければと思います。
「こんなにいろいろ貰えちゃうなら、勉強がてら行ってみようかな!」という誰かの よこしまな モチベーションになれば幸いです。
ただ、反省、そして注意点なのですが、段々とノベルティをもらうことが楽しくなってきてしまい、目的を見失いそうになるので、ご注意ください(なにぶん豪華なものも多いので……)。
以下、左からです。
タイミングだったり、抽選だったり、時間によっては配り終わっていることもあるかもしれません。ご注意ください。
NHN テコラス株式会社
アイレット株式会社
Dynatrace合同会社
日本テラデータ株式会社(かばんフック)
dataris(ARアドバンストテクノロジ株式会社)
New Relic株式会社(Tシャツ 抽選)
レッドハッド株式会社
CircleCI Japan合同会社
TIS株式会社(蒸気でアイマスク)
C-Chorus(NHN テコラス株式会社/PCクリーナー)
サイオステクノロジー株式会社
Mackerel(株式会社はてな)
AWS公式
KDDI株式会社
トレンドマイクロ株式会社
どこかのチロルチョコ(すみません……)
[Java]文字列をreplaceAllで大文字/小文字化(\u, \l)する方法
文字列をreplaceAllで大文字/小文字化(\u, \l)する方法
そもそもPatternクラスで非対応
この記事にたどり着かれた方は、以下のように書いて「大文字小文字化しようとしたけれどできなかった!」という方も恐らく含まれているのではないかと思います。
// FOO barにしたいが…… "foo bar".replaceAll("(foo)", "\\u$1") > Task :run ufoo bar
これは、String#replaceAll
にて内部的に呼び出されているPattern
が、\u, \l
に対応していないためです。
Perl 5との比較
Patternエンジンは、Perl 5と同じく、順序付けされた代替に対する従来のNFAベースのマッチングを実行します。
このクラスでサポートされていないPerl構文
...
プリプロセス演算\l \u、\L、および\U。
Java9以降なら解決策がある
Java9から、Matcher
にreplaceAll(Function<MatchResult,String> replacer)
が追加されました。
こちらを使用することで、マッチした部分を大文字、小文字化することが可能です。
Pattern.compile("(foo)") .matcher("foo bar") .replaceAll(result -> result.group(1).toUpperCase()); > Task :run FOO bar
参考
Use Java and RegEx to convert casing in a string - Stack Overflow
[Java]StreamのgroupingByで、元の順序を保つ方法
groupingByを普通に書くと
例えば、あるString
のリストがあり、それぞれの個数を数えるケースを考えます。
普通に書くと以下のようになるかと思います。
Map<String, Long> collected = List.of("d", "c", "b", "a", "d", "c", "b", "d", "c", "d") .stream() .collect(Collectors.groupingBy( Function.identity(), Collectors.counting() )); System.out.println(collected); > Task :run {a=1, b=2, c=3, d=4}
この場合、groupingBy
の結果は、List
の順序は保証されません。
これは、戻り値として使用されるMap
自体が、順序が保証されていないためです。
LinkedHashMapを使う
そこで、引数を3つ取るgroupingByを使用します
public static <T, K, D, A, M extends Map<K, D>> Collector<T,?,M> groupingBy( Function<? super T,? extends K> classifier, Supplier<M> mapFactory, Collector<? super T,A,D> downstream )
第2引数に順序が保証されるLinkedHashMap
のSupplier
を渡すことで、順序が保たれます。
Map<String, Long> collected = List.of("d", "c", "b", "a", "d", "c", "b", "d", "c", "d") .stream() .collect(Collectors.groupingBy( Function.identity(), LinkedHashMap::new, Collectors.counting() )); System.out.println(collected); > Task :run {d=4, c=3, b=2, a=1}
2引数のgroupingByの実装
参考までに、2引数のgroupingBy
の実装を見ると、内部では3引数のgroupingBy
に、順序の保たれないHashMap
を渡していることがわかります。
public static <T, K, A, D> Collector<T, ?, Map<K, D>> groupingBy(Function<? super T, ? extends K> classifier, Collector<? super T, A, D> downstream) { return groupingBy(classifier, HashMap::new, downstream); }
参考
Collectors (Java SE 17 & JDK 17)
java - Stream doesn't preserve the order after grouping - Stack Overflow
[放送大学]2022年度の受講科目と振り返り
2022度の受講科目と試験結果
1学期
履修年度 | 履修学期 | 科目コード | 科目名 | 評語 | 合否 |
---|---|---|---|---|---|
2022 | 1学期 | 1170040 | 情報学へのとびら(’22) | Ⓐ | 合格 |
2022 | 1学期 | 1140094 | 運動と健康(’22) | Ⓐ | 合格 |
2022 | 1学期 | 1150030 | 日本語リテラシー(’21) | Ⓐ | 合格 |
2022 | 1学期 | 1570269 | データベース(’17) | Ⓐ | 合格 |
2022 | 1学期 | 1570374 | 映像コンテンツの制作技術(’20) | Ⓐ | 合格 |
2022 | 1学期 | 1920014 | 色と形を探究する(’17) | Ⓐ | 合格 |
2022 | 1学期 | 1519174 | 今日のメンタルヘルス(’19) | Ⓐ | 合格 |
2022 | 1学期 | 1940015 | 音を追究する(’16) | Ⓐ | 合格 |
2学期
履修年度 | 履修学期 | 科目コード | 科目名 | 評語 | 合否 |
---|---|---|---|---|---|
2022 | 2学期 | 1140051 | 問題解決の進め方(’19) | Ⓐ | 合格 |
2022 | 2学期 | 1750054 | 日常生活のデジタルメディア(’22) | Ⓐ | 合格 |
2022 | 2学期 | 1570285 | コンピュータとソフトウェア(’18) | Ⓐ | 合格 |
2022 | 2学期 | 1570358 | Webのしくみと応用(’19) | Ⓐ | 合格 |
2022 | 2学期 | 1950029 | AIシステムと人・社会との関係(’20) | Ⓐ | 合格 |
2022 | 2学期 | 1710036 | 健康と社会(’17) | Ⓐ | 合格 |
2022 | 2学期 | 1710125 | 健康への力の探究(’19) | Ⓐ | 合格 |
2022 | 2学期 | 1710150 | 生活経済学(’20) | Ⓐ | 合格 |
印象・思い出に残った科目
1150030 日本語リテラシー(’21)
「日本語の助詞の働きを意識して読めているか」ということを、改めて考えさせられました。
特に第7回の「は」と「が」の違いについての考察が一番印象に残っています。
放送授業の終盤で出てきた、伊豆の踊子の翻訳の話は「本当に日本語が読めてますか?」ということを突き付けられた気持ちになり、ショックを受けました。是非印刷教材だけではなく、放送授業も見てほしいです。
1570269 データベース(’17)
レベル感としては、IPAのデータベーススペシャリスト試験レベルのようです。実際に章末の演習問題は、その過去問から出題をされています。
RDBMSを扱うエンジニアなら、第一正規形が良くないことは知っていると思いますが、9章の第1正規形が更新時異常を引き起こす話は、その不都合を3パターンに分けて解説しており、改めて勉強になりました。
また、8章の選択、射影、結合などの演算の厳密な定義の話も興味深かったです。
1570374 映像コンテンツの制作技術(’20)
普段よくみる映像作品が、裏ではどう作られるのか、という内容です。
長時間の映像を短い尺ににカットするときに、どう映像をつなげば不自然ではないのか、という話が12章で学べるのですが、これを読む前と読んだあとで、映像作品を見てるときに「お、あの技法使ってるのか~」なんて考えたりするようになり、見方が少し変わった気がします。
1950029 AIシステムと人・社会との関係(’20)
AIの歴史から、AIを使った結果良かったこと悪かったことの実例と、最後は人間はAIとどう付き合っていけば、という話が学べます。
AIを使う上での倫理についての話が、14, 15章に載っています。
ChatGPT等の登場により、急激にシンギュラリティが現実味を帯びてきましたが、人間には、それを使う上での高い倫理観と責任が求められるようになると感じています。
これから、AI抜きでの生活というのはおそらく不可能なので、総合科目として、技術者にもそうでない人にも、広くいろいろな人にお勧めできます。
1750054 日常生活のデジタルメディア(’22)
この科目は内容ではなく、単位認定試験が記憶に残っています。
放送大学の単位認定試験は以下の3つのパターンがあると思っています。
- 印刷教材をちゃんと勉強すれば解ける
- 印刷教材だけではなく、放送授業まで視聴し理解する必要がある
- このパターンの場合、印刷教材と放送授業の内容が違う
- 印刷教材・放送授業の両方を勉強しても厳しい
- そもそも授業の内容が難しかったり、授業でやっていない問題が出たり
この科目は2のパターンでした、特に2022年1学期の過去問は、試験問題の大半が放送授業の内容(しかも結構細かいところ)から出されており、非常に焦りました。
実際にその試験の平均点は63.7点とかなり低く、当時のTwitterでも、難しかったという投稿が上がっていました。
#単位認定試験 #放送大学
— さとーさん@放送大学'21秋生 (@taman_ouj) July 17, 2022
日常生活のデジタルメディア 難しかった〜
受講の際も教科書の内容が放送と差分があるというか、教科書にないことも言ってる…と思っていたが。
…とだけ言っておきます。
日常生活のデジタルメディア・・
— tomi@ouj (@tomix0321) July 18, 2022
不合格続出なのでは😑
単位取得試験期間が終了したのでネタバレ🆗ですよね?日常生活のデジタルメディアがめっちゃ難しかった。単位取れる気がしない。過去問と全然違う。問題ってランダム出題なのかな?となると当たり外れとかありそうだけど。#放送大学#日常生活のデジタルメディア
— 断然☆猫派@ouj (@liecchi_2403) July 26, 2022
放送授業を視聴しながらスクショやノートを取り試験に臨み、実際には1学期ほどは難しくなかった(と個人的には感じた)ものの、やはり放送授業の細かい点から出題され、一筋縄ではいかないと感じた科目でした。
ちなみに2022年1学期は、科目改定後初めての試験であり、試験を作る側としても、科目を頑張って作ったという想いが試験にあふれてしまったのかな……、なんて感じました。
内容としては良い科目でしたので、特に腰を据えて学習できる方にはおすすめです。
終わりに
この記事が、単位選択の際の何らかのヒントになれば幸いです。
[放送大学]放送授業のスクリーンショットが真っ黒になるときに読むページ
放送授業のスクリーンショットが真っ黒になってしまう時には
皆様、先日は単位認定試験お疲れさまでした。
さて表題の件、試験前に放送授業を見ながら、大事そうなところをメモ代わりにスクリーンショットにて撮っていたのですが、ある時まで撮れていたのが、ちょっと休憩をはさんだ後、急にできなくなってしまいました。
調べたところ、ハードウェア アクセラレーションに起因するもののようです。
例えばChromeの場合、 設定 > システム > ハードウェア アクセラレーションが使用可能な場合は使用する よりチェックを外すと、私の場合はスクリーンショットが撮れるようになりました。
ただ、この設定を外すと、YouTubeなど他の動画サイトを視聴の際に、PCが重くなることが考えられます。
そういう時は、また設定を有効にしてみてください。