(Juriya o ukeireru shoutaijou)

「魅力的な美容ジュリアを受け入れる正体情報」

Python愛好者、科学計算の魔術師、データサイエンティストへの熱烈な招待

Juliaは、一般目的、ダイナミック、高性能かつ高レベルなプログラミング言語で、ジャストインタイムコンパイルされます。この言語は、2018年にメジャーなバージョン1.0がリリースされた比較的新しい言語です。この記事では、データサイエンス、科学計算に興味がある方や熱心なPythonユーザーである方には、この言語が絶対に武器に加える価値があることを実証します。もしかしたら、これはあなたが出会うであろう最も美しいプログラミング言語かもしれません。

ギャラクシー、紫色、緑色、赤色の惑星デジタルアート- DALLE 2で生成されました

この記事では、Juliaのアイデアの高みと学習の価値について説明します。終了したら、次の記事「PythonからJuliaへの究極のガイド」を読むことを強くおすすめします。これは、PythonからJuliaへの簡単な移行に役立ちます。

目次

· Juliaは高レベルです基本構文数学用のエレガントな構文· Juliaは高速ですベンチマーク言語の二重問題ジャストインタイムコンパイル· Juliaは式の問題を解決します式の問題マルチディスパッチ具象型と抽象型· Juliaは完全な機能を備えています配列のサポート文字列のサポートマルチスレッディングCコードとの簡単な統合標準ライブラリ· Juliaは一般目的です紹介自動化とスクリプティング· Juliaは非常に拡張可能です紹介マクロ· まとめ

Daniele Levis Pelusi氏による写真(Unsplashから)

Juliaはハイレベルです

既に紹介で感じたかもしれませんが、JuliaはPythonと同様、汎用性のある動的で高水準の言語です。確かめるために、基本的なJuliaコードがPythonと比べてどのようなものか見てみましょう。

基本構文

次のPythonのゲームを考えてみましょう。

import randomdef guessing_game(max):    random_number = random.randint(1, max)    print(f"1から{max}の間の数を予想してください")    while True:        user_input = input()        guess = int(user_input)        if guess < random_number:            print("小さすぎます")        elif guess > random_number:            print("大きすぎます")        else:            print("正解です!")            break        guessing_game(100)

これがJuliaの等価なコードです。

function guessing_game(max::Integer)    random_number = rand(1:100)      println("1から$maxの間の数を予想してください")    while true        user_input::String = readline()        guess = parse(Int, user_input)        if guess < random_number            println("小さすぎます")        elseif guess > random_number            println("大きすぎます")        else            println("正解です!")            break        end    endendguessing_game(100)

ここでの主な違いは、Juliaはインデントを仮定せず、コロンを必要とせず、かわりに if 条件やループ、関数のスコープを終了させるための明示的な “end” を必要とする点です。MatlabやFortranから来た方にとっては馴染みのある感じでしょう。

もう一つ気づいたかもしれない違いは、Juliaは変数宣言や関数引数(および返り値の型、ただし稀に使用)に型注釈を自然にサポートしていることです。 これらは常にオプションですが、一般的には型のアサーションに使用され、同じメソッドが複数の型にオーバーロードされている場合に呼び出すべき適切なメソッドインスタンスをコンパイラが選択し、一部の変数や構造体宣言でのパフォーマンス上の利点に使用されます。

数学のためのエレガントな構文

# エレガントな式 x = 2z = 2y + 3x - 5# Unicode公式対応α, β, γ = 1, 2, π/2# 1行関数f(r) = π*r^2f'(3)  # 導関数(Flux.jlパッケージを使用)# 列ベクトルはまさに列v₁ = [1      2      3      4]  v₂ = [1 2 3 4]# 転置println(v1' == v2)# これはまさに3x3行列M⁽ⁱ⁾ = [1 2 3        4 5 7        7 8 9]# 欠損値の明示的なモデリングX = [1, 2, missing, 3, missing]

JuliaがPythonよりも優れている重要な点の一つは、数学の構文サポートです。変数に定数を乗算する際には * を使用する必要がありませんし、変数名にはLaTeX記号がサポートされています(\pi を π に変換するためにVSCodeの拡張機能を使用する必要があるかもしれません)。また、一般的にはコード定義での行列のレイアウトに合わせて、行列自体も尊重されます。

例えば、ニューラルネットワークの勾配降下法を実装する場合。

Pythonでは、おそらく次のように書くでしょう。

import numpy as np# ニューラルネットワークの勾配降下法J_del_B_n = [np.zeros(b) for b in B_n]J_del_W_n = [np.zeros(W) for W in W_n]for (x, y) in zip(x_batch, y_batch):    J_del_B_n_s, J_del_W_n_s = backprop(x, y)    J_del_B_n = [J_del_b + J_del_b_s for J_del_b,                 J_del_b_s in zip(J_del_B_n, J_del_B_n_s)]    J_del_W_n = [J_del_W + J_del_W_s for J_del_W,                 J_del_W_s in zip(J_del_W_n, J_del_W_n_s)]d = len(x_batch)W_n = [(1 - lambda_val * alpha / d) * W - lambda_val /       d * J_del_W for W, J_del_W in zip(W_n, J_del_W_n)]B_n = [(1 - lambda_val * alpha / d) * b - lambda_val /       d * J_del_b for b, J_del_b in zip(B_n, J_del_B_n)]

もちろん、Pythonでこのようなコードを書くこともできますが、多くのエディターではUnicode変数の周りに黄色の四角を表示したり(または強調表示を行わなかったり)し、また、Pickleなどのサードパーティのパッケージでコードが動作しなくなる場合もあります。

Solaiman Hossenによる写真(Unsplashから)

Juliaは速い

Python、Rubyなどの他の高水準言語とは異なり、JuliaがPythonの夢の実現であると考えられるもう1つの主な理由は、高水準であるために速度を犠牲にしないということです。実際、CやC++などの低水準言語と同じくらい高速です。

ベンチマーク

参考までに、以下にJuliaと他の言語の人気のあるパフォーマンスベンチマークを報告します:

Juliaマイクロベンチマーク:JuliaLangによるイメージ - MITライセンス

2つの言語の問題

Juliaのパフォーマンスに関連する結果として、2つの言語の問題が解消されます:

  • 研究コード(たとえば、機械学習モデル)は通常、高水準言語であるPythonで記述されます。これは、高水準で対話的であり、そのため、科学に注力することができ(コードの問題が少ないため)、より多様な探索が可能になります。
  • 研究コードが最終的になった後、それを本番環境に展開する前に、Cなどの低水準言語に書き換える必要があります。

問題は、同じコードを複数の言語で書き換える必要があることです。これは一般的に困難でエラーの原因になる可能性があります。たとえば、研究コードが展開された後に修正された場合、最悪の場合は低水準言語で再びすべて書き直す必要があります。

この問題を解決する1つの方法は、パフォーマンスが重要なライブラリ(たとえば、Numpy)をCなどの低水準言語で書き、内部的にCを呼び出すPythonの関数でそれらをラップすることです。これにより、パフォーマンスに気を使うことなく、研究コードと本番環境の両方で使用することができます。実際には、これは非常に限られています。なぜなら:

  • 新しい開発者が書いた革新的な科学的手法で貢献したり協力したりするのは非常に難しいです。なぜなら、それらを高レベルのライブラリに公開する前に、パフォーマンスのためにCなどの低レベルの言語でそれらを書き直す必要があるかもしれないからです。
  • 科学計算のドメインで高レベルの言語の開発者には厳しい制約が課されることがあります。例えば、明示的なforループの記述は非常に desh害されるかもしれません。

Juliaは高レベルでインタラクティブであり、製品にもかかわらず非常に高速です。

Juliaはジャストインタイムコンパイルです

Juliaのパフォーマンスに関連する小さな注意点があります。JuliaはJITコンパイルされているため、一連のJuliaコードの最初の実行は完了するまでに時間がかかります。この時間内に、すべての関数コードが、コードから推論された特定の変数タイプに対して(つまり、プロセッサが解釈できるコードに)変換されます。一度変換が完了すると、コンパイルされた表現がキャッシュされます。そのため、同じタイプの異なる入力で関数が再度呼び出される場合、すぐに解釈されます。

さらに詳しく説明すると、N個の引数を持つ関数では、N個の引数のタイプの組み合わせごとに可能なネイティブコード表現が指数関数的に存在します。Juliaは、コードが実行される最初の時点でコードから推論されるタイプに対応する表現に関数をコンパイルします。一度コンパイルが完了すると、関数の追加の呼び出しは楽に行えます。タイプの推論には型注釈(オプションであり、他の目的も持つ可能性がある)を必ずしも使用しないことに注意してください。タイプは入力の実行時の値から推論される場合があります。

これは問題ではありません。研究コードやサーバー上で実行されるコードは最初にコンパイルする必要があり、一度それが完了したら、コードの追加の実行(実際のAPI呼び出しやさらなる実験など)が非常に高速に行えます。

Thom Milkovicさんの写真

Juliaは表現の問題を解決する

表現の問題

表現の問題は、表すデータ抽象を拡張することができるかどうかについてのものです(つまり、サポートされているタイプやメソッド)。つまり、表現の問題の解決策は、次のことを可能にします:

  • 既存の操作が適用される新しいタイプの追加
  • 既存のタイプに適用される新しい操作の追加

これにより、既存の操作のコードを変更せずに新しいタイプを追加できるし、既存のタイプのコードを変更せずに新しい操作を追加できることを意味します。

Pythonは、多くの他のプログラミング言語と同様にオブジェクト指向であり、表現の問題を解決できません。

次のようなデータ抽象があると仮定しましょう:

# 基底クラスclass Shape:    def __init__(self, color):        pass    def area(self):        pass# 子クラスclass Circle(Shape):    def __init__(self, radius):        super().__init__()        self.radius = radius    def area(self):        return 3.14 * self.radius * self.radius

既存のメソッドが適用される新しいタイプを追加するのは非常に簡単です。単にShapeの基底クラスを継承します。既存のコードの変更は必要ありません:

class Rectangle(Shape):    def __init__(self, width, height):        super().__init__()        self.width = width        self.height = height    def area(self):        return self.width * self.height

一方、既存のタイプに適用される操作を追加するのは簡単ではありません。もしperimeterというメソッドを追加したい場合、基底クラスとこれまでに実装されたすべての子クラスを変更する必要があります。

この問題の結果として、パッケージxが著者Xによって保守され、最初は操作のセットSxをサポートし、もう一つの操作セットSyが開発者Yのセットに役立つ場合、Yの開発者はこれらのメソッドを追加するためにXのパッケージを変更できる必要があります。実際には、開発者Yは独自に別のパッケージを作成するかもしれません。Xの開発者は、さらなるコードの保守が増えることを喜ばないかもしれませんし、Syは同じパッケージに含める必要がない別のジャンルのメソッドかもしれません。

一方、既存の操作が適用される新しいタイプを簡単に追加できるため、開発者Yは、単にXが実装しているタイプで操作を実装する新しいタイプを定義したい場合、パッケージxを変更したり、コードを重複させる必要もなく簡単にこれを行うことができます。単にタイプをimportして、それを継承するだけです。

マルチディスパッチ

さまざまなパッケージ間での大規模な統合を可能にする「式の問題」を解決するために、Juliaは従来のオブジェクト指向プログラミングを完全に排除します。クラスの代わりに、Juliaは抽象型定義、structs(抽象型のカスタムタイプのインスタンス)、そして我々が後で見るように完璧に式の問題を解決するマルチディスパッチという技術を使用します。

上記の同等のものを見てみましょう:

### Shape抽象型(インターフェース)abstract type Shape endfunction area(self::Shape)  end### Circleタイプ(インターフェースを実装)struct Circle <: Shape    radius::Float64endfunction area(circle::Circle)    return 3.14 * circle.radius^2end

ここで、「Shape」という抽象型を定義しました。それが抽象であるということは、具体的にはインスタンス化できないことを意味しますが、他のタイプ(クラス)はそれをサブタイプ(継承元)にすることができます。その後、タイプ「円」を定義しました。これは「Shape」という抽象型のサブタイプであると指定し、入力は「Circle」の型であることを指定して「area」メソッドを定義しました。これにより、次のようなことができます。

c = Circle(3.0)println(area(c))

これにより、「28.26」と表示されます。しかし、「c」は「Shape」でもあるため、両方の「area」定義を満たすのですが、2番目の定義の方がより具体的なので、コンパイラはその呼び出しに選択します。

クラスベースのOOPと同様に、既存のコードを変更することなく別のタイプ「矩形」を追加することも簡単です:

struct Rectangle <: Shape    length::Float64    width::Float64endfunction area(rect::Rectangle)    return rect.length * rect.widthend

そして、

rect = Rectangle(3.0, 6.0)println(area(rect))

「18.0」となります。これが動作中のマルチディスパッチです。メソッドの正しいインスタンスは、引数の実行時の型に基づいて動的にディスパッチされます。CやC++のバックグラウンドを持っている場合、これは関数のオーバーロードを思い出させるでしょう。違いは、関数のオーバーロードが動的ではなく、コンパイル時に見つかる型に依存することです。したがって、その動作が異なる例を考え出すことができます。

さらに重要なのは、クラスベースのOOPとは異なり、ファイルを変更する必要なくShapeCircleRectangleのいずれかにメソッドを追加できることです。上記のすべてのファイルが自分のパッケージ内にある場合、私のパッケージをインポートするだけで、ShapeCircleRectangleのタイプにアクセスし、新しい関数を書いて、それらを独自の「ShapeVisuals」パッケージにエクスポートできます。

### インターフェースの定義function animate(self::Shape)  endfunction ThreeDify(self::Shape)  end### Circleの定義function animate(self::Circle)  ...endfunction ThreeDify(self::Circle)  ...end### Rectangleの定義function animate(self::Rectangle)  ...endfunction ThreeDify(self::Rectangle)  ...end

よく考えてみると、これと知っているOOPの主な違いは、パターンがfunc(obj, args)の代わりにobj.func(args)を従うことです。さらに、func(obj1, obj2, args)のようなことも簡単です。もう1つの違いは、メソッドとデータを一緒にカプセル化することも、それらに保護を課すこともないということです。おそらく、開発者が十分に成熟しており、コードがレビューされる場合には関係のない措置です。

抽象型と具象型

抽象型がインスタンス化できない型であり、他の型がサブタイプになることを示すことを知ったことは、Juliaの型システムを議論するための道を開きます。変数の宣言時、関数の引数または戻り値として型を注釈するための構文var::typeの使用はオプションです。

Juliaのすべての型は、前述のように抽象型または具象型のいずれかです。具象型は、前述で定義したようにカスタム型などのようにインスタンス化できる型です。

Juliaには、次のような階層型システムがあります。

Juliaマイクロベンチマーク:Julia for Optimization and LearningのJoolaによる画像、MITライセンスで使用

関数が1つの引数を取り、任意の数値に対して操作する場合、func(x::Number)を使用します。これは、文字列などの非数値の値が渡された場合にのみエラーをスローします。一方、任意の浮動小数点数に対してのみ機能する場合は、func(x::AbstractFloat)を使用します。入力がBigFloat, Float64, Floar32 or Floar16のいずれかの型の場合、エラーはスローされません。多重ディスパッチが存在するため、与えられた数値が整数である場合のケースを処理するために別の関数のインスタンスfunc(x::Integer)を定義することもできます。

Juliaには、AbstractStringなどの他の抽象型に対しても類似の階層型システムがありますが、ずっとシンプルです。

Photo by Paul Melki on Unsplash

考えてみると、Pythonはデフォルトの状態ではほとんど機能がありません。たとえば、Numpyなどの人気のあるパッケージを使用せずにPythonだけを使用してデータサイエンスや科学計算を行うことはほとんどできません。この分野のほとんどの他のパッケージもNumpyに依存しています。彼らはすべて以下のように言語の一部であるかのように「Numpy」配列型(デフォルトのPythonリスト型ではなく)を使用および想定しています。

Juliaはそうではないです。Numpyに似た配列サポートがデフォルトで付属しており、ブロードキャストおよびベクトル化サポートも含まれています。たとえば、以下は主要なNumpy操作とJuliaでのネイティブな書き方を比較しています。

#> 1. NumPy配列の作成### Pythonarr = np.array([[1, 2, 3],                [4, 5, 6],                [7, 8, 9]])### Juliaarr = [1 2 3       4 5 6       7 8 9]#> 2. 配列の形状を取得### Pythonshape = arr.shape### Juliashape = size(arr)#> 3. 配列の形状を変更### Pythonreshaped_arr = arr.reshape(3, 3)### Juliareshaped_arr = reshape(arr, (3, 3))#> 4. インデックスで要素にアクセス### Pythonelement = arr[1, 2]### Juliaelement = arr[1, 2]#> 5. 要素ごとの算術演算### Pythonmultiplication = arr * 3### Juliamultiplication = arr .* 3# 6. 配列の結合### Pythonarr1 = np.array([[1, 2, 3]])arr2 = np.array([[4, 5, 6]])concatenated_arr = np.concatenate((arr1, arr2), axis=0)### Juliaarr1 = [1 2 3]arr2 = [4 5 6]concatenated_arr = vcat(arr1, arr2)#> 7. ブールマスキング### Pythonmask = arr > 5masked_arr = arr[mask]### Juliamask = arr .> 5masked_arr = arr[mask]#> 8. 配列要素の合計の計算### Pythonmean_value = arr.sum()### Juliamean_value = sum(arr)

文字列サポート

Juliaは、文字列と正規表現の広範なサポートも標準で備えています:

name = "Alice"age = 13## 文字列の連結greeting = "Hello, " * name * "!"## 補間message2 = "来年、$(age + 1)歳になります。"## 正規表現text = "以下はいくつかのメールアドレスです: [email protected]"# メールアドレスの正規表現を定義するemail_pattern = r"[\w.-]+@[\w.-]+\.\w+"# メールアドレスをマッチングするemail_addresses = match(email_pattern, text)"aby" > "abc"       # true

文字列を比較する場合、辞書順(一般的なアルファベット順)で後ろに表示されるものが、前に表示されるものよりも大きいと見なされます。Perlなどの高度な文字列処理言語でできることのほとんどは、Juliaでもできることが示されています。

マルチスレッド

Pythonが真の並列マルチスレッディングをサポートしていない理由は、グローバルインタプリタロック(GIL)によるものです。これにより、インタプリタがスレッドセーフを保証するために同時に複数のスレッドを実行することはできません。複数のスレッド間を切り替えることは可能ですが(たとえば、サーバースレッドがネットワークリクエストを待機している間に、インタプリタは別のスレッドに切り替えることができます)。

幸いにも、Pythonから呼び出すCプログラムでこのロックを解放することは簡単であり、それがNumpyが可能な理由です。ただし、巨大な計算ループがある場合、Pythonコードを並列に実行して計算を高速化することはできません。Pythonの悲しい現実は、行列などの大規模データ構造に適用されるほとんどの数学的操作は並列化可能であるということです。

一方、Juliaでは真の並列マルチスレッディングがネイティブにサポートされており、次のように簡単に行うことができます:

# マルチスレッディング前for i in eachindex(x)    y[i] = a * x[i] + y[i]end# マルチスレッディング後Threads.@threads for i in eachindex(x)    y[i] = a * x[i] + y[i]end

コードを実行する際には、システムで利用可能なスレッドの数を指定することができます。

Cコードとの簡単な統合

JuliaからCコードを呼び出すプロセスは、公式にサポートされており、Pythonよりも効率的かつ簡単に行うことができます。次のコードを呼び出したい場合

#include <stdio.h>int add(int a, int b) {    return a + b;}

この関数をJuliaで呼び出すための主なステップは、次のように書くことです

# 関数、戻り値の型、引数の型、入力を指定result = ccall(add, Cint, (Cint, Cint), 5, 3)

これをPythonで行うのははるかに難しく、効率も低下します。特に、Juliaの型や構造体をCのそれに簡単にマッピングすることができるためです。

これにより、ここでCオブジェクトコードを出力できるほとんどの言語を実行することが可能です。通常、それらのための外部のよく知られたパッケージが存在します。たとえば、Pythonコードを呼び出すには、次のようにPyCall.jlパッケージを使用できます:

using PyCallnp = pyimport("numpy")# PythonでNumPy配列を作成py_array = np.array([1, 2, 3, 4, 5])# NumPy配列でいくつかの操作を実行py_mean = np.mean(py_array)py_sum = np.sum(py_array)py_max = np.max(py_array)

このためには、パッケージのインストール以外にほとんど事前の設定は必要ありません。同様のパッケージを使用して、Fortran、C++、R、Java、Mathematica、Matlab、Node.jsなどの関数を呼び出すことも可能です。

一方で、PythonからJuliaを呼び出すことも可能ですが、より洗練された方法ではありません。これは恐らく、Cで実装することなく関数を高速化するために使用されることがあるでしょう。

標準ライブラリ

Juliaには、あらかじめインストールされているパッケージがいくつかあります(明示的に読み込む必要があります)。これには、StatisticsパッケージとLinearAlgebraパッケージ、インターネットへのアクセスに使用するDownloadsパッケージ、分散コンピューティング(Hadoopのようなもの)向けのDistributedパッケージ、コードの最適化を支援するProfileパッケージ、ユニットテスト用のTestsパッケージ、パッケージ管理用のPkgパッケージなどが含まれます。

私は、Pythonの熱心なユーザーであり、Pythonで複数のパッケージを開発してきました。Pythonのサードパーティのパッケージ「Setuptools」とJuliaのPkgは比較になりませんが、Juliaのほうが非常にクリーンで使いやすいです。Pythonには独自のパッケージ管理とテストツールがないのは理解できませんでした。これらはプログラミング言語にとって本当に基本的なニーズです。

写真:Tom MによるUnsplash

Juliaは汎用です

導入

以前にJuliaに触れたことがある場合、Juliaは科学計算がドメインであるドメイン特化言語だと思っているのは自然なことです。確かに、Juliaは科学計算のために表現力豊かで効率的に設計されていますが、それが一般的な用途には適していないわけではありません。ただし、科学計算を念頭に置いて構築されています。言語は一般的にどの程度汎用的であることができるかには度合いがあります。たとえば、Juliaはデータサイエンスや機械学習、Web開発、自動化やスクリプト、ロボット工学のほか、科学計算以外の用途にも使用することができますが、PythonのPygameのようなゲーム開発のための成熟したパッケージはまだ存在しません。JuliaのパッケージGenie.jlはFlaskとほぼ同等であるが、Djangoのようなより包括的なフレームワークには及ばないかもしれません。つまり、Juliaが現時点では望むほど汎用的ではないとしても、その点を念頭に置いて構築され、将来的には汎用的になることが期待されています。

自動化とスクリプト

Juliaは自動化とスクリプトに使用できることを述べましたが、エレガントなシェルのような構文でそれを実現しています。

たとえば、Juliaで実行できるファイルシステムとプロセス操作の一部を次に示します:

# ディレクトリを作成mkdir("my_directory")# 作業ディレクトリを変更cd("my_directory")# 現在のディレクトリのファイルをリスト表示println(readdir())# ディレクトリを削除rm("my_directory"; recursive=true)# ファイルが存在するかどうかをチェックif isfile("my_file.txt")    println("ファイルが存在します。")else    println("ファイルが存在しません。")end# Juliaからシェルのコマンドを実行run(`echo "Hello, Julia!"`)# シェルのコマンドの出力をキャプチャresult = read(`ls`, String)println("現在のディレクトリの内容: $result")

ターミナルで実際に書く内容との類似性に注目してください。

Starry Nightの代替デジタルアート- DALLE 2を使用して作者が生成したもの

Juliaは非常に拡張可能です

導入

LISPプログラミング言語の美しい特徴の1つは、ホモイコニックであることです。つまり、コードはデータと同様に扱えるため、通常の開発者によって言語に新しい機能や意味論を追加することができます。Juliaもホモイコニックに設計されています。たとえば、Juliaは多重ディスパッチのみをサポートすると言いましたが、ObjectOriented.jlパッケージを使ってJuliaでオブジェクト指向プログラミングを記述することができます。さらに、新しい型を作成する場合、新しい型と基本関数や演算子(単なる関数)をオーバーロードして新しい型と一緒に使用することも簡単です。

マクロ

Juliaのマクロのサポートは、これが可能になる主な理由です。マクロは、プログラムのパース時に実行されるコードを返す関数と考えることができます。次のようなマクロを定義するとします:

macro add_seven(x)    quote        $x + 7    endend

機能に似た方法で、次のように呼び出すことができます:

x = 5@add_seven x

これは12を返します。内部で起こっていることは、パースタイム(コンパイル前)に、マクロが実行され、5 + 7というコードを返しており、コンパイルタイムには12に評価されるということです。マクロは、さまざまな機能を動的に実行するための方法と考えることができます。

別の実践的なユースケースでは、10の有用なメソッドを持つパッケージがあるとしましょう。このパッケージに新しいインタフェースを追加する必要があり、それにより10個のストラクト(各メソッドに対して1つずつ)を書く必要があります。対応する関数が与えられた場合にそのどれでもストラクトを書くことがシステマティックであると仮定すると、10個の関数に対してコードを生成するために1つのマクロを簡単に作成することができます。実際には、書いたコードは一般的な方法で単一のストラクトを書くことと同等であり、これにより時間を節約できます。

マクロが可能であることにより、さらに多くのマジックが実現されます。例えば、前述のように、Threads.@threadsマクロを使用してforループをマルチスレッド化することができます。関数呼び出しの実行時間を測定するには、@time func()とするだけです。もしBenchmarkToolsパッケージを使用している場合は、@benchmark func()は関数を多く呼び出し、時間に関する統計情報や小さなプロットを返します。もしメモ化という概念を知っている場合、それを単純な@memoizeマクロで任意の関数に適用することができます。何かを修正する必要はありません。さらに、@code_native func()を使用すると、関数によって生成されたネイティブコードが表示され、他のマクロではコンパイルプロセス中のコードの他の表現を示すものもあります。

ラップアップ

Juliaについて話した言語のすべての機能は、元々Juliaの計画の一部でした。Juliaのウェブサイトに記載されているように、これが言語のビジョンです:

「オープンソースで自由なライセンスを持つ言語が欲しい。Cの速度とRubyのダイナミズムが欲しい。Lispのような本当のマクロを持ちながら、Matlabのような明らかでなじみ深い数学表記が欲しい。Pythonとして一般的なプログラミングに使いやすく、Rとして統計に使いやすく、Perlとして文字列処理に使いやすく、Matlabとして線形代数に優れていて、シェルとしてプログラムをつなぐことが得意であるものが欲しい。非常にシンプルに学ぶことができ、一番真剣なハッカーを幸せにすることができるものが欲しい。対話的であり、コンパイルされるものが欲しい。」

このストーリーを読んだことで、ビジョンステートメントに言及された各単語について考えることができるようになったはずです。

Julia言語についてもっと学び、この言語を学ぶことを検討していただけたら幸いです。それでは次回まで、さようなら。

We will continue to update VoAGI; if you have any questions or suggestions, please contact us!

Share:

Was this article helpful?

93 out of 132 found this helpful

Discover more