×

日常の色々な事

開発の考えや映画やアニメなど、ごちゃごちゃしたものを書いてます。「本サイトはアフィリエイトが含まれています」

mojoの使い方(基本編)

pythonっぽいのに処理速度が速く、pythonライブラリも使えるmojoについて基本をまとめました。 プログラム初心者やmojopython初心者の人に参考になるかと思います。

プログラム実行方法

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の文法

mojopythonっぽい言語なので、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: IntInt は型定義で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の構成

同フォルダ別ファイルの関数・構造体の呼び出し方

ソースコードの役割ごとにファイルを分けることは、プログラムでは良くあることです。 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

pythonへの連携

自作関数の呼び出し

mojopythonと親和性がある言語なので、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ライブラリの呼び出し

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の結果はちゃんと帰ってきています。