9
3

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.

Juliaのモジュール周り

Posted at

Juliaのモジュール

いつも自分でよくわからなくなるので、マニュアルをまとめてみる。

モジュールは、Juliaのコードの単位で、階層化可能な名前空間である。

module モジュール名
中身
end
  • 一般にモジュールの中身はインデントしない。全部インデントすることになってしまうから。

  • モジュール名とファイル名には一般に対応関係は無い。1つのファイルに複数のモジュールを
    書くこともあるし、下のようにして1つのモジュールを複数に分けることもある。

module SomeModule
include("file1.jl")
include("file2.jl")
end
  • モジュール名は型の名前と同じで、UpperCamelCaseのように、大文字で始めるキャメルケースで書き、
    型の名前との競合を防ぐため、可能なら複数形にする。
module FastThings
struct FastThing
    ...
end
end

修飾名

モジュールの名前空間はネストし階層化することができる。

module A
  y = 0
  module B
    x = 0
  end
end

ある名前の親モジュールを参照するにはparentmodule関数を用いる。

julia> parentmodule(UnitRange)
Base
  • モジュールの外から名前にアクセスするには、Base.UnitRangeのようにモジュール名を前につなげる。デリミタは.
  • シンボルのみの名前の場合はパーザの都合で:も必要。Base.:+のように書く。また==などの一部の演算子はBase.:(==)のようにカッコも必要。

export

モジュールが公開する名前はexportリストに書く。exportは通常モジュールの冒頭に書くがそうしなければならないわけではない。また、このリストを複数書くこともできる。ここにモジュールの提供する外部インターフェイスを記述することが期待されている。

ただし、エクスポートされていない名前でも修飾名を使えばいつでもアクセスできるので、モジュールに隠蔽の機能はない。このあたり実にJuliaっぽい。exportリストにある名前は、後述のusingを用いた際に名前空間に取り込まれるというだけで、importだけ使っている場合にはあまり意味がない。

module A
export x, y
x = 0
y = 1
z = 2
end

このように書くと、モジュールAxyは公開されているがzは公開されていない状態になる。

julia> using .A
julia> x
0
julia> y
1
julia> z
ERROR: UndefVarError: z not defined

xyはアクセスできるようになったが、zは出来ていない。ただし修飾名を使えばzにもアクセスできる。

julia> A.z
2

ここでusing .Aは、同じレベルにあるモジュールAを利用するという宣言。using Aで良さそうな気がするがだめ。モジュールAは、implicitなモジュールMainのサブモジュールMain.Aとして定義されている。usingでなにもつけないと絶対パス指定になるので、Aだけでは指定できないということらしい。using Main.AならOK。

モジュールとパッケージ

Juliaにはモジュールと独立にパッケージがある。パッケージはひとかたまりになったライブラリで、特定の構造を持つディレクトリに存在し、トップレベルにパッケージと同名のモジュールを持たなければならない。また、Project.tomlというファイルがなければならない。

パッケージを作るには、Julia REPLのパッケージモードのcreateコマンドを用いると簡単。REPLから]をタイプするとパッケージモードになる。

pkg> generate HelloWorld

こうすると、HelloWorldというディレクトリの中にProject.tomlsrc/HelloWorld.jlが作られる。REPLから;をタイプするとシェルモードに入るので、そこで確認してみよう。.jlファイルにはテンプレートがすでにできている。

shell> tree HelloWorld
HelloWorld
├── Project.toml
└── src
    └── HelloWorld.jl

1 directory, 2 files

shell> cat HelloWorld/src/HelloWorld.jl
module HelloWorld

greet() = print("Hello World!")

end # module

shell> 

ここでは、ディレクトリ名がパッケージ名と同じになっているが、これは必須ではない。任意のディレクトリ名が使用できる。

これでパッケージが一応できたわけだが、これだけでは使用できない。

julia> import HelloWorld
ERROR: ArgumentError: Package HelloWorld not found in current path:

Juliaにこのパスにこのようなライブラリがあることを知らせる必要がある。それには、パッケージモードでactivateコマンドを用いる。

(@v1.7) pkg> activate HelloWorld
  Activating project at `~/juliatest/HelloWorld`

これでimportできるようになった。

julia> import HelloWorld
[ Info: Precompiling HelloWorld [9c493a92-d783-4ef9-b670-e336ee782ab6]

julia> HelloWorld.greet()
Hello World!

importとusing

Juliaにはモジュールやパッケージを使用可能にする方法として、importusingの2つが用意されている。

importはパッケージを読み込むために用いる。同じソースコード上のモジュールはすでに読み込まれているので、改めてimportすることにはあまり意味はない。

usingimportの機能に加えて、そのモジュールでexportされている名前を、現在の名前空間に読み込む機能を持つ。

julia> module A
       export x
       x = 1
       end
Main.A

julia> x
ERROR: UndefVarError: x not defined

julia> import .A

julia> x
ERROR: UndefVarError: x not defined

julia> using .A

julia> x
1

名前を指定した読み込み

明示的に読み込む名前を指定することもできる。exportされていなくても読み込める。これはimportでもusingでも同じようにできる。

julia> module A
       x = 1
       end
Main.A

julia> import .A: x

julia> x
1

この書き方だと、なぜかモジュール名自体は名前空間に取り込まれない。

julia> module A
       module B
       x = 1
       end
       end
Main.A

julia> using .A.B: x

julia> B
ERROR: UndefVarError: B not defined

こういう場合は using .A.B: x, Bのように書けばいいらしい。

import でも usingでも全く効果が同じなわけではなく、importでないと駄目なケースがある。モジュール内の関数にメソッドを追加するには、importでないといけない。

julia> module A
       function x(i::Int64) x end
       end
Main.A

julia> import .A: x

julia> function x(i::Float64) x end
x (generic function with 2 methods)

これをusingでやろうとすると、こうなる。

julia> using .A: x

julia> function x(i::Float64) x end
ERROR: error in method definition: function A.x must be explicitly imported to be extended
Stacktrace:
 [1] top-level scope
   @ none:0
 [2] top-level scope
   @ REPL[3]:1

不用意に定義を追加できないようにという趣旨なのだろう。でも、xは見えているのにメソッドが追加できないということなので、どうも見え方にも種類があるらしい。ややこしい話だ。そもそも、定義を追加するときには明示的にパッケージを指定するようにしたほうが良さそう。

julia> using .A

julia> function A:x(i::Float64) x end

as によるリネーム

名前空間に読み込むときにすでに同名の名前があると衝突して読み込めない。その場合にはasを用いてリネームする。

julia> module A
       x = 0
       end
Main.A

julia> x = 1
1

julia> import .A: x
WARNING: import of A.x into Main conflicts with an existing identifier; ignored.

julia> import .A: x as x0

julia> x, x0
(1, 0)

モジュールをまるごとリネームすることもできる。

julia> import .A as X

julia> X.x
0
9
3
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
9
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?