F#のすすめ
静的型付け言語と動的型付け言語
 静的型付けとは?
 型が決定するのはコンパイル時
 パフォーマンスは高め
 記述は比較的面倒くさくなる
 例
 C#
 Java
 動的型付けとは?
 型が決定するのは実行時
 パフォーマンスは低め
 その代わりお手軽
 例
 Python
 JavaScript
 そもそも「型」って何よ?
 真剣に語りだすと本が一冊書けるレベル
 あとあんまり詳しくない🙄
 なのでざっくり行きます。
静的型付け言語と動的型付け言語
 静的型付き言語の一つ C#の場合
静的型付け言語と動的型付け言語
using System.Collections.Generic;
using System.Linq;
using static System.Console;
namespace Sample
{
class Program
{
private static void Main()
{
if (!int.TryParse(ReadLine(), out int n)) return;
IEnumerable<int> numbers = Enumerable.Range(1, n);
int sumOfOdds = numbers.Where((int x) => { return x % 2 != 0; }).Sum();
WriteLine(sumOfOdds);
}
}
}
 動的型付き言語の一つ Rubyの場合
静的型付け言語と動的型付け言語
s = gets.chomp
exit if s =~ /D/
numbers = 1..s.to_i
sum_of_odds = numbers.select(&:odd?).sum
puts sum_of_odds
C# に比べて
めっちゃお手軽
 動的型付き言語の一つ Python と JavaScript の場合
静的型付け言語と動的型付け言語
try:
n = int(input())
except ValueError:
exit()
numbers = (x + 1 for x in range(0, n))
sum_of_odds = sum(x for x in numbers if x % 2)
print(sum_of_odds)
process.stdin.on('data', chunk => {
const n = +chunk.toString();
if (Number.isNaN(n)) return;
const numbers = Array.from({ length: n }, (_, i) => i + 1);
const sumOfOdds = numbers.filter(x => x % 2).reduce((a, b) => a + b);
console.log(sumOfOdds);
});
静的型付け言語と動的型付け言語
 なぜこんなにも記述量に差があるのか。
 型が明示されていて、その分コード量が増える。
 ただし、コードに型が明示されるということは、後から読んだときに分かりやすいということでもある。
 C# はたまたまクラスベースの言語であり、クラスの宣言も必要だった。
 加えて名前空間の宣言も省略していなかった。
 ※ C# Scripts (csx) を使うと名前空間やクラスの宣言が不要になるので、Rubyのコードとほとんど変わらなくなる。
型推論という仕組みがある
 コードの文脈に沿って、その式の型を明示しなくても型が一意に決まる機能
 つまり動的型付け言語では無縁の機能
private static void Main()
{
if (!int.TryParse(ReadLine(), out var n)) return;
var numbers = Enumerable.Range(1, 10);
var sumOfOdds = numbers.Where(x => x % 2 != 0).Sum();
WriteLine(sumOfOdds);
}
private static void Main()
{
if (!int.TryParse(ReadLine(), out int n)) return;
IEnumerable<int> numbers = Enumerable.Range(1, 10);
int sumOfOdds = numbers.Where((int x) => { return x % 2 != 0; }).Sum();
WriteLine(sumOfOdds);
}
型推論を活用すればコードがどんどん短くなるのでは??
そこで F# ですよ
 F# の場合
let n =
match System.Int32.TryParse(stdin.ReadLine()) with
| false, _ -> exit 1
| true, n -> n
let numbers = [1 .. n]
let sumOfOdds = numbers |> List.filter (fun x -> x % 2 <> 0) |> List.sum
printfn "%d" sumOfOdds
Q. あんまり他と変わってなくない?
A. すみません、例が悪かったです。
次は F# の型推論の強力さに
スポットしていきます。
※ 対比として C# のコードも載せます。
F# の型推論
open System.Collections.Generic
let list = List()
list.Add 1
list.Add 5
list.Add "abc" // ← コンパイルエラー
using System.Collections.Generic;
var list = new List<int>();
list.Add(1);
list.Add(5);
list.Add("abc"); // ← コンパイルエラー
public interface IMixed { }
public class Int : IMixed
{
public int Value { get; }
public Int(int value) => Value = value;
public void Deconstruct(out int n) => n = Value;
}
public class Str : IMixed
{
public string Value { get; }
public Str(string value) => Value = value;
public void Deconstruct(out string s) => s = Value;
}
var list = new List<IMixed>();
list.Add(new Int(1));
list.Add(new Int(5));
list.Add(new Str("abc"));
foreach (var x in list)
{
Console.WriteLine(x switch
{
Int(var n) => $"[{n}]",
Str(var s) => $""{s}"",
_ => throw new ArgumentException(),
});
}
数字と文字列両方持てる配列を定義したいこともあるよね
type Mixed = Int of int | Str of string
let list = ResizeArray ()
list.Add <| Int 1
list.Add <| Int 5
list.Add <| Str "abc"
list
|> Seq.iter (function
| Int n -> printfn "[%d]" n
| Str s -> printfn ""%s"" s)
using System;
using System.Collections.Generic;
var list = new List<object>();
list.Add(1);
list.Add(5);
list.Add("abc");
foreach (var x in list)
{
switch (x)
{
case int n:
Console.WriteLine($"[{n}]");
break;
case string s:
Console.WriteLine($""{s}"");
break;
}
}
let f x =
match x with
| 1 -> "One"
| 2 -> "Two"
| 3 -> "Three"
| _ -> "More"
let g = function
| 1 -> "One"
| 2 -> "Two"
| 3 -> "Three"
| _ -> "More"
bool f(int x)
{
switch (x)
{
case 1: return "One";
case 2: return "Two";
case 3: return "Three";
default: return "More";
}
}
F# の型推論
let inline parseOpt input =
let mutable x = Unchecked.defaultof<_>
let succeeded = (^a : (static member TryParse : string * ^a byref -> bool) (input, &x))
if succeeded then Some x else None
parseOpt "123"
|> Option.iter (printfn "%d")
parseOpt "123"
|> Option.iter (printfn "%f")
if (int.TryParse("123", out var i))
{
Console.WriteLine(i);
}
if (double.TryParse("123", out var d))
{
Console.WriteLine(d);
}
 FizzBuzz
 基本ルール
 通常 1 から 100 までの数字を数え上げて出力するだけ
 ただし !
 3 の倍数のときには、数字を出力する代わりに "Fizz" と出力する
 5 の倍数のときには、数字を出力する代わりに "Buzz" と出力する
 15 の倍数のときには、数字を出力する代わりに "FizzBuzz" と出力する
みなさんもやってみませんか?
let fizzBuzz n =
match n % 3, n % 5 with
| 0, 0 -> "FizzBuzz"
| 0, _ -> "Fizz"
| _, 0 -> "Buzz"
| _, _ -> string n
[1 .. 100]
|> List.map fizzBuzz
|> List.iter (printfn "%s")
fizzBuzz :: Int -> String
fizzBuzz n =
case (n `mod` 3, n `mod` 5) of
(0, 0) -> "FizzBuzz"
(0, _) -> "Fizz"
(_, 0) -> "Buzz"
(_, _) -> show n
main :: IO ()
main = mapM_ (putStrLn <$> fizzBuzz) [1 .. 100]
const fizzBuzz = n => {
if (n % 15 === 0) return 'FizzBuzz';
if (n % 3 === 0) return 'Fizz';
if (n % 5 === 0) return 'Buzz';
return String(n);
};
for (const s of Array.from({ length: 100 }, (_, i) => i + 1)) {
console.log(fizzBuzz(s));
}
def fizz_buzz n
s = ''
s << 'Fizz' if (n % 3).zero?
s << 'Buzz' if (n % 5).zero?
s.empty? ? n.to_s : s
end
(1 .. 100).each do |n|
puts fizz_buzz n
end
def fizz_buzz(n):
if n % 15 == 0:
return "FizzBuzz"
elif n % 3 == 0:
return "Fizz"
elif n % 5 == 0:
return "Buzz"
else:
return str(n)
for s in (fizz_buzz(n) for n in range(1, 101)):
print(s)
type FizzBuzzResult = Num of int | Fizz | Buzz | FizzBuzz
let fbResultToString = function Num n -> string n | r -> string r
let (|Num|Fizz|Buzz|FizzBuzz|) n =
match n % 3, n % 5 with
| 0, 0 -> FizzBuzz
| 0, _ -> Fizz
| _, 0 -> Buzz
| _, _ -> Num n
let fizzBuzz = function
| Num n -> Num n
| Fizz -> Fizz
| Buzz -> Buzz
| FizzBuzz -> FizzBuzz
[1 .. 100]
|> List.map (fizzBuzz >> fbResultToString)
|> List.iter (printfn "%s")

F#のすすめ

  • 1.
  • 2.
    静的型付け言語と動的型付け言語  静的型付けとは?  型が決定するのはコンパイル時 パフォーマンスは高め  記述は比較的面倒くさくなる  例  C#  Java  動的型付けとは?  型が決定するのは実行時  パフォーマンスは低め  その代わりお手軽  例  Python  JavaScript
  • 3.
     そもそも「型」って何よ?  真剣に語りだすと本が一冊書けるレベル あとあんまり詳しくない🙄  なのでざっくり行きます。 静的型付け言語と動的型付け言語
  • 4.
     静的型付き言語の一つ C#の場合 静的型付け言語と動的型付け言語 usingSystem.Collections.Generic; using System.Linq; using static System.Console; namespace Sample { class Program { private static void Main() { if (!int.TryParse(ReadLine(), out int n)) return; IEnumerable<int> numbers = Enumerable.Range(1, n); int sumOfOdds = numbers.Where((int x) => { return x % 2 != 0; }).Sum(); WriteLine(sumOfOdds); } } }
  • 5.
     動的型付き言語の一つ Rubyの場合 静的型付け言語と動的型付け言語 s= gets.chomp exit if s =~ /D/ numbers = 1..s.to_i sum_of_odds = numbers.select(&:odd?).sum puts sum_of_odds C# に比べて めっちゃお手軽
  • 6.
     動的型付き言語の一つ Pythonと JavaScript の場合 静的型付け言語と動的型付け言語 try: n = int(input()) except ValueError: exit() numbers = (x + 1 for x in range(0, n)) sum_of_odds = sum(x for x in numbers if x % 2) print(sum_of_odds) process.stdin.on('data', chunk => { const n = +chunk.toString(); if (Number.isNaN(n)) return; const numbers = Array.from({ length: n }, (_, i) => i + 1); const sumOfOdds = numbers.filter(x => x % 2).reduce((a, b) => a + b); console.log(sumOfOdds); });
  • 7.
    静的型付け言語と動的型付け言語  なぜこんなにも記述量に差があるのか。  型が明示されていて、その分コード量が増える。 ただし、コードに型が明示されるということは、後から読んだときに分かりやすいということでもある。  C# はたまたまクラスベースの言語であり、クラスの宣言も必要だった。  加えて名前空間の宣言も省略していなかった。  ※ C# Scripts (csx) を使うと名前空間やクラスの宣言が不要になるので、Rubyのコードとほとんど変わらなくなる。
  • 8.
    型推論という仕組みがある  コードの文脈に沿って、その式の型を明示しなくても型が一意に決まる機能  つまり動的型付け言語では無縁の機能 privatestatic void Main() { if (!int.TryParse(ReadLine(), out var n)) return; var numbers = Enumerable.Range(1, 10); var sumOfOdds = numbers.Where(x => x % 2 != 0).Sum(); WriteLine(sumOfOdds); } private static void Main() { if (!int.TryParse(ReadLine(), out int n)) return; IEnumerable<int> numbers = Enumerable.Range(1, 10); int sumOfOdds = numbers.Where((int x) => { return x % 2 != 0; }).Sum(); WriteLine(sumOfOdds); }
  • 9.
  • 10.
  • 11.
     F# の場合 letn = match System.Int32.TryParse(stdin.ReadLine()) with | false, _ -> exit 1 | true, n -> n let numbers = [1 .. n] let sumOfOdds = numbers |> List.filter (fun x -> x % 2 <> 0) |> List.sum printfn "%d" sumOfOdds
  • 12.
  • 13.
    次は F# の型推論の強力さに スポットしていきます。 ※対比として C# のコードも載せます。
  • 14.
    F# の型推論 open System.Collections.Generic letlist = List() list.Add 1 list.Add 5 list.Add "abc" // ← コンパイルエラー using System.Collections.Generic; var list = new List<int>(); list.Add(1); list.Add(5); list.Add("abc"); // ← コンパイルエラー
  • 15.
    public interface IMixed{ } public class Int : IMixed { public int Value { get; } public Int(int value) => Value = value; public void Deconstruct(out int n) => n = Value; } public class Str : IMixed { public string Value { get; } public Str(string value) => Value = value; public void Deconstruct(out string s) => s = Value; } var list = new List<IMixed>(); list.Add(new Int(1)); list.Add(new Int(5)); list.Add(new Str("abc")); foreach (var x in list) { Console.WriteLine(x switch { Int(var n) => $"[{n}]", Str(var s) => $""{s}"", _ => throw new ArgumentException(), }); } 数字と文字列両方持てる配列を定義したいこともあるよね type Mixed = Int of int | Str of string let list = ResizeArray () list.Add <| Int 1 list.Add <| Int 5 list.Add <| Str "abc" list |> Seq.iter (function | Int n -> printfn "[%d]" n | Str s -> printfn ""%s"" s) using System; using System.Collections.Generic; var list = new List<object>(); list.Add(1); list.Add(5); list.Add("abc"); foreach (var x in list) { switch (x) { case int n: Console.WriteLine($"[{n}]"); break; case string s: Console.WriteLine($""{s}""); break; } } let f x = match x with | 1 -> "One" | 2 -> "Two" | 3 -> "Three" | _ -> "More" let g = function | 1 -> "One" | 2 -> "Two" | 3 -> "Three" | _ -> "More" bool f(int x) { switch (x) { case 1: return "One"; case 2: return "Two"; case 3: return "Three"; default: return "More"; } }
  • 16.
    F# の型推論 let inlineparseOpt input = let mutable x = Unchecked.defaultof<_> let succeeded = (^a : (static member TryParse : string * ^a byref -> bool) (input, &x)) if succeeded then Some x else None parseOpt "123" |> Option.iter (printfn "%d") parseOpt "123" |> Option.iter (printfn "%f") if (int.TryParse("123", out var i)) { Console.WriteLine(i); } if (double.TryParse("123", out var d)) { Console.WriteLine(d); }
  • 17.
     FizzBuzz  基本ルール 通常 1 から 100 までの数字を数え上げて出力するだけ  ただし !  3 の倍数のときには、数字を出力する代わりに "Fizz" と出力する  5 の倍数のときには、数字を出力する代わりに "Buzz" と出力する  15 の倍数のときには、数字を出力する代わりに "FizzBuzz" と出力する
  • 18.
  • 19.
    let fizzBuzz n= match n % 3, n % 5 with | 0, 0 -> "FizzBuzz" | 0, _ -> "Fizz" | _, 0 -> "Buzz" | _, _ -> string n [1 .. 100] |> List.map fizzBuzz |> List.iter (printfn "%s") fizzBuzz :: Int -> String fizzBuzz n = case (n `mod` 3, n `mod` 5) of (0, 0) -> "FizzBuzz" (0, _) -> "Fizz" (_, 0) -> "Buzz" (_, _) -> show n main :: IO () main = mapM_ (putStrLn <$> fizzBuzz) [1 .. 100]
  • 20.
    const fizzBuzz =n => { if (n % 15 === 0) return 'FizzBuzz'; if (n % 3 === 0) return 'Fizz'; if (n % 5 === 0) return 'Buzz'; return String(n); }; for (const s of Array.from({ length: 100 }, (_, i) => i + 1)) { console.log(fizzBuzz(s)); } def fizz_buzz n s = '' s << 'Fizz' if (n % 3).zero? s << 'Buzz' if (n % 5).zero? s.empty? ? n.to_s : s end (1 .. 100).each do |n| puts fizz_buzz n end def fizz_buzz(n): if n % 15 == 0: return "FizzBuzz" elif n % 3 == 0: return "Fizz" elif n % 5 == 0: return "Buzz" else: return str(n) for s in (fizz_buzz(n) for n in range(1, 101)): print(s)
  • 21.
    type FizzBuzzResult =Num of int | Fizz | Buzz | FizzBuzz let fbResultToString = function Num n -> string n | r -> string r let (|Num|Fizz|Buzz|FizzBuzz|) n = match n % 3, n % 5 with | 0, 0 -> FizzBuzz | 0, _ -> Fizz | _, 0 -> Buzz | _, _ -> Num n let fizzBuzz = function | Num n -> Num n | Fizz -> Fizz | Buzz -> Buzz | FizzBuzz -> FizzBuzz [1 .. 100] |> List.map (fizzBuzz >> fbResultToString) |> List.iter (printfn "%s")