5
6

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

Elixir、Rust、JavaScriptの構文の比較

Last updated at Posted at 2023-10-19

Elixir、Rust、javascriptの構文の比較

標準出力に出力

大体

ElixirではIO.putsを使用する。

IO.puts "Hello, World."

Rustではprintln!マクロを使用する。

println!("Hello, World");

JavaScriptでコンソールに出力したいならconsole.log()を使用する。

console.log("Hello, World");

反復処理

for

Elixirは要素とイテレータを<-で区切る。
形式: for 要素 <- イテレータ do ... end

Rustは要素とイテレータをinで区切る。
形式: for 要素 in イテレータ {}

JavaScriptは要素とイテレータを()で囲み、inで区切る。
形式: for (要素 in イテレータ) {}

while

Elixirにはwhileloop存在しない
なので再帰関数を使用するとよい。

defmodule Loop do

  def while(0) do
    :ok
  end

  def while(num) do
    IO.puts "Hello, World!"
    while(num - 1)
  end
end

Loop.while(10)

Rust

while 条件 {
  ...
}

loop {
  ...
  break;
}

JavaScript

while (条件) {

}

制御構文

if

Elixirのcondを使用するとコンパクトに記述できる。
論理積、論理和にandorを使用する

if :a == :a do
  ...
else
  ...
end

if :a == :b, do: ..., else: ... 

x = :c

cond do
  x == :a or x == :c -> ...
  x == :b -> ...
  true    -> ...
end

Rustでは論理積、論理和に&&||を使用する。

if "a" == "a" {
  ...
} else if "b" == "b" || "c" == "d" {
  ...
}

Javascriptのifは条件式を()で囲む。
論理積、論理和に&&||を使用する。

if ("a" === "c") {
  ...
} else if ("b" === "b" || "c" === "d") {
  ...
}

"a" === "a" ? ... : ...

パターンマッチ

Elixirではcaseを使用する。
また->を使用し、条件と処理を区切る。

デフォルトの処理は_で記述する。

x = :a

case x do
  :a  -> ...
  :b  ->
    ...
  _   -> ...
end

Rustではmatchを使用する。
また=>を使用し、条件と処理を区切る。
,で区切る。

デフォルトの処理は_で記述する。

let x = "a";

match x {
  "a" => ...,
  "b" => {
    ...
  }
  _   => {}
}

JavaScriptではswitchを使用する。
また条件をcaseで記述する。

デフォルトの処理はdefalutで記述する。

let x = "a";

switch (x) {
  case "a":
    ...
    break;
  case "b":
    ...
    break;
  default:
    ...
} 

リスト(と配列)

ElixirのListは異なるデータ型を格納することができる。

list = [1, :ok, "hello"]

IO.puts "[0]: #{Enum.at list, 0}, [1]: #{Enum.at list, 1}, [2]: #{Enum.at list, 2}" 
# [0]: 1, [1]: ok, [2]: hello

RustのVecは型安全によって異なるデータ型を持つことができない

ただしstd::any::Anyを使用しBox<dyn Any>型にすることにより異なるデータを保持することができる。(Boxについて)

// 実行時に型が決定
let list = vec![1, 2, 3]

use std::any::Any;

let list_with_any: Vec<Box<dyn Any>> = vec![
    Box::new(1 as i32), 
    Box::new("a".to_string()), 
    Box::new(false)
];  

list_with_any
.iter()
.for_each(|item| {
    if let Some(integer) = item.downcast_ref::<i32>() {
        print!("[0]: {:?}, ", integer)
    } else if let Some(string) = item.downcast_ref::<String>() {
        print!("[1]: {}, ", string)
    } else if let Some(boolean) = item.downcast_ref::<bool>() {
        print!("[2]: {:?}", boolean)
    }
});

// 配列
let l: [isize; 4] = [1,2,3,4];
println!("{:?}", l);

JavaScriptのArray(配列)は異なるデータ型を格納することができる。

let array = [1, "a", false];

// バッククォート(``)で囲む
console.log(`[0]: ${array[0]}, [1]: ${array[1]}, [2]: ${array[2]}`);

リスト内包

Elixirでは以下のように記述してリスト内包を行う。

形式: 変数 = for 要素名 <- 1..5, do: 値の計算
例 : list = for x <- 1..5, do: x * 2

Rustにはリスト内包が存在しない
が以下のよう記述することはできる。

let list: Vec<u32> = 
  (1..5)
    .map(|i| i * 2)
    .collect();

JavaScriptにはリスト内包がない。

マップ

Elixirでは異なる型のキーと値を保持できる。
2種類の記述があり、どちらかに統一して記述する。

m1 = %{"name" => "Taro", "age" => 34, :adress => "Tokyo"}
# すべてのキーがアトムの場合
m2 = %{email: "aaa@aaa.com", blood_type: "X"}

Rustではstd::collections::HashMapを使用する。
異なるキーと値の型を保持したいときはBox型を使用する。(Boxについて)

use std::collections::HashMap;

let mut map: HashMap<String, String> = HashMap::new();

map.insert("name".to_string(), "Taro".to_string());
map.insert("age".to_string(), "34".to_string());

println!("{:?}", map);

// 値をBox型にする
let mut map_with_box: HashMap<String, Box<dyn Any>> = HashMap::new();

map_with_box.insert("name".to_string(), Box::new("Taro".to_string()));
map_with_box.insert("age".to_string(), Box::new(34 as u32));

// downcast_refで値を取得
&map_with_box
  .iter()
  .for_each(|(k, v)| {
      if let Some(string) = v.downcast_ref::<String>() {
          print!("{}: {} ", k, string);
      } else if let Some(integer) = v.downcast_ref::<u32>() {
          print!("{}: {:?} ", k, integer);
      }
  });

JavaScriptではMapクラスを使用する。

let map = new Map();

map.set("name", "Taro");
map.set("age", 34)

構造体(とオブジェクト)

Elixirはフィールドのデータ型を宣言しない
名前付きマップと比べて、フィールドが欠けていることを防止できる。

defmodule User do
  # 初期値を定義している。
  defstruct name: nil, age: nil
end

taro = %User{name: "Taro", age: 34}

# 名前付きmap
satoshi = %{name: "Satoshi", age:38}

Rust

struct User {
  name: Stirng,
  age: u32
}

struct Address(String);

let taro = User { name: "Taro", age: 34 };

JavaScriptでは一般的に定義と生成を同時に行うほうが一般的。

// 定義と生成を同時に行っている
const taro1 =  {
  name: "Taro",
  age: 34
}

// オブジェクトの定義を行っている
const userTemplate = {
  name: "",
  age: null
}

const taro2 = Object.create(userTemplate)
taro2.name ="Taro"
taro2.age = 34

文字列

結合

Elixirで<>を使用する。

t = "World."

s = "Hello, " <> t

Rustでは+を使用する。なお、String(文字列)と&str(文字列スライス)の順でないと結合できない。

let t: &str = "World.";

let s = "Hello, ".ti_string() + t;

JavaScriptでは+で結合する。

let t = "World."

let s = "Hello" + t

関数

JavaScriptでは戻り値をreturnで

無名関数

Elixirではfn関数式を使用するだけ。

形式: 変数 = fn 引数 -> 処理 end

func = fn i -> i * i end

func.(10)

long_func = fn i ->
  ans = i * i
  IO.puts "result: #{ans}"
end

long_func.(10)

Rustではクロージャ(||)を使用して無名関数を定義する。

なお型推論が行われるため戻り値の型を記述する必要はない。

形式: let 変数 = |変数: 型| {一行であれば{}は必要ない };

let func = |i: i32| i * i;

func(10);

let long_func = |i: i32| {
  let ans = i * i;
  println!("result: #{}", ans);
};

long_func(10);

JavaScriptではアロー関数を使用することが一般的(2つ目)

形式: let 変数 = (変数) => {処理}

// function()を用いる
let func = function(i) { return i * i }

func(10)

// arrow()関数を使用して簡略化
// functionを省略できる。
// 引数が一つなら()も省略できる。
let arrow_long_func = i => { 
  let ans =  i * i;
  console.log(ans); 
}

arrow_long_func(10)

イテレーション処理、高階関数

イテレーション処理と高階関数について

Elixirでは|>を使用して関数の結果を別の関数に渡すことができる。

Enum.each()なので注意。

list = [1,2,3,4,5]

list 
  |> Enum.each(fn i ->
    // ここで処理
    if rem(i,2) == 0 do
      IO.puts i
    end
  end)

list
  |> Enum.filter(fn i -> rem(i, 2) == 0 end)
  |> Enum.map(fn i -> IO.puts i end)

Rust

.for_each()なので注意。

let list: Vec<u32> = vec![1,2,3,4,5];

for i in list.iter() {
  if i % 2 == 0 {
    println!("{}", i);
  }
}

list.iter().filter(|&i| i % 2 == 0).for_each(|&i| println!("{}", i));

JavaScript

let list = [1, 2, 3, 4, 5];

for (i in list) {
  if (i % 2 === 0) {
    console.log(i);
  }
}

list
  .filter(i => i % 2 === 0)
  .forEach(i => console.log(i))

try、エラー処理

Elixirではtryを使用する。各記述は以下の用途で使用する。

  • rescue - エラー、特定のエラータイプごとの処理を行う。
  • catch - throw()で処理を受け取った値を処理。
  • else - throw()で値を受け取らなかったときの処理。
  • after - try文のあとに行う処理を記述。

Javascriptのcatchとは意味が異なるので注意。

try do
  # ここで処理
  # throw()で値をcatchに渡すことができる
rescue
  エラー -> エラーの処理
catch
  # throwで値を受け取ったときの処理  
  x -> ...
else
  # throwで値を受け取らなかったときの処理
  _ -> ...
after
  # try文のあとに行う処理を記述
end

RustのResult型エラーハンドリングのための型である。
Rustでは例外が発生するような関数の戻り値はResult型である。
ので、matchで処理する。

x = ... // ここでResult型が戻り値の関数を実行しているものとする。

match x {
  Ok(_) => // 処理
  Err(_) => // 例外処理
}

JavaScriptのcatchはエラーを処理する

try {
  // 処理
} catch (e) {
  // エラー処理
} finally {
  // 後処理
}

日付

NaiveDateTimeは省略(多用したらよくないらしい)

Elixirではタイムゾーンを指定するためにtzdatatimexをインストールする必要がある。

to_day = Date.utc_today()
utc_now = DateTime.utc_now()

tokyo_time = DateTime.now("Asia/Tokyo")

Rustではchronoクレートを使用する。またタイムゾーンを使用するためにchorono_tzを使用する。

use chrono::{Utc, Local};
use chrono_tz::Asia::Tokyo;

let to_day = Utc::now.date_naive();
let utc_now = Utc::now();

// タイムゾーンがAsia/Tokyoであれば、以下2つは同じ
let tokyo_time = Utc.now().with_timezone(&Tokyo);
let local_time = Local::now();

JavaScriptではDateクラスを使用する。

let to_day = new Date();

用語

Box

  1. Rustにおいて値はスタックに保存される。
  2. Box<T>を生成することで、値はヒープに保存される。またスタックにはヒープへのポインタが保存される。
  3. Vecはコンパイル時に格納するデータ型(Vec<T>のTのこと)が決まっている必要がある。
  4. よってVecがBox型を格納することで、異なるデータ型の要素を格納することができる。

イテレーション処理と高階関数

大体はイテレーション処理 ≒ 高階関数

2つは以下の点で異なる。

  • イテレーション処理はforを使用して一要素ごとに処理を行うイメージ。
  • 高階関数は関数ごとにすべての要素を処理するイメージ。
5
6
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
6

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?