pythonっぽいのに処理速度が速く、pythonライブラリも使えるmojoについて基本をまとめました。
プログラム初心者やmojo・python初心者の人に参考になるかと思います。
プログラム実行方法
mojoのプログラムを実行する方法は以下の2つがあります。
- REPL :コードを記入すると即時応答が返ってくる方式
- ソースファイル:コードを記載したファイルを読み込ませて実行する方式
REPL
コードを入力すると即時応答が返ってくる方式です。
シンプルなソースの確認であったり、データ分析コンテストなど試行錯誤する場合などによく使われます。
mojo
コマンドを打つとREPLモードになるので、後はソースを1行ずつ書けばいいです。
実行したいときは何も入力せずにエンターを押せば結果が返ってきます。
例1:console文字列を画面に表示する
user@DESKTOP:/mnt/d/program/mojo/src$ mojo
Welcome to Mojo! 🔥
Expressions are delimited by a blank line.
Type `:quit` to exit the REPL and `:mojo help` for further assistance.
1> print('console ')
2.
console
例2:1+2の結果を画面に表示する
13行目でprintにより3(aの結果)が表示された後に、14行目にa=1+2の結果が表示されています。
user@DESKTOP:/mnt/d/program/mojo/src$ mojo
Welcome to Mojo! 🔥
Expressions are delimited by a blank line.
Type `:quit` to exit the REPL and `:mojo help` for further assistance.
1> print('console ')
2.
console
2> a = 1 + 2
3. print(a)
4.
3
(Int) a = 3
4>
ソースファイル
ファイルにソースコードを入力後にまとめて実行する方式です。
他の人がプログラムを実行する際や複数の人で開発をする場合などに使用されます。
ソースファイルの作成
mojoのソースファイルは拡張子がmojo
または 🔥
になります。
実行されるのはmain
関数になるので、これがない場合エラーになります。
例:hello worldのソース
fn main():
print("Hello, world!")
ソースファイルの実行
ソースファイルの実行にはmojoに上のソースファイルを食わせば実行できます。
例:hello.mojoとhello.🔥の実行
user@DESKTOP:/mnt/d/program/mojo/src/hello$ mojo hello.mojo
Hello, world!
user@DESKTOP:/mnt/d/program/mojo/src/hello$ mojo hello.🔥
Hello, world!
バイナリファイル
作成したソースコードをバイナリファイルにコンパイルする方式です。
ソースコードを渡さなくても良いので中身を見られずに相手にプログラムを実行してもらったり、たくさんのソースコードを渡さずにコンパイル済みの1ファイルだけ渡すなどに利用されます。
バイナリファイルの作成
バイナリファイルの作成はmojo build {ソースファイル名} -o {バイナリファイル名}
で出来ます。
例:hello.mojoファイルをtestバイナリファイルへコンパイル
user@DESKTOP:/mnt/d/program/mojo/src/hello$ mojo build hello.mojo -o test
user@DESKTOP:/mnt/d/program/mojo/src/hello$
バイナリファイルの実行
一般的なファイルと同じく./{ファイル名}
で実行できます。
例:testファイルの実行
user@DESKTOP:/mnt/d/program/mojo/src/hello$ ./test
user@DESKTOP:/mnt/d/program/mojo/src/hello$ Hello, world!
mojoはpythonっぽい言語なので、pythonを知っている人にはスムーズに適用できるかと思います。
python自体もわかりやすい初心者向き言語なので、mojoもわかりやすい初心者向き言語になります。
関数
mojoにも関数があります。関数は色々な処理をまとめたものになり、適切な関数名を付けるととても便利です。
例:sample funcを画面に表示する関数を呼び出すソース
sample funcを画面に表示する機能を持ったsample_funcと言う関数をmainから呼び出しています。
fn sample_func():
print("sample func")
fn main():
print("start main")
sample_func()
print("end main")
実行結果
user@DESKTOP:/mnt/d/program/mojo/src/func_class$ mojo func_main.mojo
start main
sample func
end main
例:引数を足す関数を呼び出すソース
引数を足して呼び出し元に返却する機能を持ったsample_plus_func
関数をmain
から呼び出して画面に表示しています。
ちなみに(arg1: Int
の Int
は型定義でarg1はInt型だということを表していて -> Int:
はInt型を返すことを表しています。
fn sample_plus_func(arg1: Int, arg2: Int) -> Int:
return arg1 + arg2
fn main():
print("start main")
let result = sample_plus_func(1, 2)
print(result)
print("end main")
実行結果
user@DESKTOP:/mnt/d/program/mojo/src/func_class$ mojo func_main.mojo
start main
3
end main
クラス(struct)
mojoにはクラスがありませんが、代わりにstructと言うクラスっぽいものがあります。
使い方はあまり変わりませんが、コンパイルタイミングが異なります。pythonのクラスは動的に定義されるそうですが、mojoのstructはコンパイル時に使用されるため動的な構造の変更ができません。
例:コンストラクタで与えた引数を足して返す関数を持つ構造体をmain
で呼び出して画面に表示しています。
struct SampleClass:
var first: Int
var second: Int
fn __init__(inout self, first: Int, second: Int):
self.first = first
self.second = second
fn plus(self) -> Int:
return self.first + self.second
fn main():
print("start main")
let sampleClass = SampleClass(1, 2)
let result = sampleClass.plus()
print(result)
print("end main")
実行結果
user@DESKTOP:/mnt/d/program/mojo/src/func_class$ mojo struct_main.mojo
start main
3
end main
同フォルダ別ファイルの関数・構造体の呼び出し方
ソースコードの役割ごとにファイルを分けることは、プログラムでは良くあることです。
mojoでも別ファイルの関数や構造体などを呼び出すことができます。
分けた場合はfrom {ファイル名} import {関数名/構造体名}
を定義すればさっきと同じように使えます。
例:上で例に挙げた関数とクラスを別ファイルにしてmainから呼び出す
フォルダ構成
./
- func_sample.mojo:関数だけのファイル
- struct_sample.mojo:構造体だけのファイル
- func_struct_main.mojo:上の2つを呼び出すファイル
呼び出す側のファイル
from func_sample import sample_plus_func
from struct_sample import SampleClass
fn main():
print("start main")
var result = sample_plus_func(1, 2)
print(result)
let sample_class = SampleClass(2, 3)
result = sample_class.plus()
print(result)
print("end main")
実行結果
user@DESKTOP:/mnt/d/program/mojo/src/func_class$ mojo func_struct_main.mojo
start main
sample func plus
3
sample class plus
5
end main
別フォルダ別ファイルの関数・構造体の呼び出し方
ソースコードが大きくなるにしたがって、ファイルだけでは役割分担が不足してきます。そういう場合にはフォルダに意味を持たせるのもプログラムでは良くあることです。
mojoでも別フォルダの別ファイルから関数や構造体などを呼び出すことができますが、ちょっとした作法が必要なので注意してください。
フォルダを分けた場合は、フォルダ内に空でもいいので__init__.mojo
ファイルを作り、呼び出す際はfrom {ディレクトリ名}.{ファイル名} import {関数名/構造体名}
を定義すればさっきと同じように使えます。python2と同じ感じです
例:上で例に挙げた関数とクラスを別フォルダと別ファイルにしてmainから呼び出す
フォルダ構成
./
- sub_dir/
- __init__.mojo:mojoにソースフォルダだと認識させるファイル
- func_sample.mojo:関数だけのファイル
- struct_sample.mojo:構造体だけのファイル
- func_struct_main.mojo:上の2つを呼び出すファイル
呼び出す側のファイル
from sub_dir.func_sample import sample_plus_func
from sub_dir.struct_sample import SampleClass
fn main():
print("start main")
var result = sample_plus_func(1, 2)
print(result)
let sample_class = SampleClass(2, 3)
result = sample_class.plus()
print(result)
print("end main")
実行結果
user@DESKTOP:/mnt/d/program/mojo/src/other_dir$ mojo func_struct_main.mojo
start main
sample func plus
3
sample class plus
5
end main
自作関数の呼び出し
mojoはpythonと親和性がある言語なので、pythonの呼び出しが簡単に行えます。
pythonライブラリを使って、インポートをしてあげればいいだけです。
具体的にはPython.add_to_path({pythonファイルまでのパス})
でパスを定義して、Python.import_module({ファイル名})
で取り込むと関数が使えます。
注意事項としてはtry except
で囲まないとコンパイルエラーが出るので囲んでください。
例:画面にhello pythonと表示するpython関数をmojoから呼び出す
フォルダ構成
./
- call_dir/
- call_python.py:pythonファイル
- python_call_main.mojo:mojoのメイン
pythonファイル
def print_python():
print('hello python')
def plus(arg1: int, arg2: int) -> int:
return arg1 + arg2
mojoファイル
from python import Python
from python.object import PythonObject
fn main():
print("start main")
try:
# pythonファイルまでのディレクトリを記載
Python.add_to_path("call_dir")
# pythonファイルを読み込み
let mypython = Python.import_module("call_python")
# 関数を実行
mypython.print_python()
let result = mypython.plus(1, 2)
print(result)
except e:
print(e)
print("end main")
結果
user@DESKTOP:/mnt/d/program/mojo/src/other_dir$ mojo python_call_main.mojo
/mnt/d/program/mojo/first_time/src/python_call/python_call_main.mojo:12:28: warning:
'PythonObject' value is unused
mypython.print_python()
~~~~~~~~~~~~~~~~~~~~~^~
_ =
start main
hello python
3
end main
ワーニングは出てますが、結果はちゃんと帰ってきています。
自作クラスの呼び出し
自作関数と同じようなノリでpythonの自作クラスも呼び出せます。
例:画面にhello python classと表示するpython関数と足し算をする関数を持ったクラスをmojoから呼び出す
フォルダ構成
./
- call_dir/
- call_class_python.py:pythonファイル
- python_call_main.mojo:mojoのメイン
pythonファイル
class SampleClass:
a = 0
b = 0
def __init__(self, a: int, b:int) -> None:
self.a = a
self.b = b
def plus(self, c: int) -> None:
return self.a + self.b + c
def print(self) -> None:
print('hello python class')
mojoファイル
from python import Python
from python.object import PythonObject
fn main():
print("start main")
try:
# pythonファイルまでのディレクトリを記載
Python.add_to_path("call_dir")
# pythonファイルを読み込み
let mypython = Python.import_module("call_class_python")
# クラスを生成
let sample_class = mypython.SampleClass(4, 5)
# クラスの関数を実行
sample_class.print()
let result = sample_class.plus(6)
print(result)
except e:
print(e)
print("end main")
結果
user@DESKTOP:/mnt/d/program/mojo/src/other_dir$ mojo python_call_main.mojo
/mnt/d/program/mojo/first_time/src/python_class_call/python_call_main.mojo:13:25: warning: 'PythonObject' value is unused
sample_class.print()
~~~~~~~~~~~~~~~~~~^~
_ =
start main
hello python class
15
end main
ワーニングは出てますが、結果はちゃんと帰ってきています。
pythonはライブラリが豊富なことでも有名です。mojoになってもその恩恵を受けられるようにpythonライブラリをmojoでも使えるようになっています。
今までやってきたpythonを呼び出す方法とほぼ同じで出来ます。
例:numpyを呼び出してランダムの行列を作ってサイズを表示する
mojoファイル
from python import Python
fn main():
print("start main")
try:
# pythonファイルを読み込み
let np_python = Python.import_module("numpy")
# 関数を実行
let num1 = np_python.random.random((2, 3))
let result = num1.size
print(num1)
print(result)
except e:
print(e)
print("end main")
結果
user@DESKTOP:/mnt/d/program/mojo/src/other_dir$ mojo python_call_main.mojo
start main
[[0.53263209 0.44010483 0.96346389]
[0.80562149 0.96406495 0.46062812]]
6
end main
numpyの結果はちゃんと帰ってきています。