2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Nim で競プロをやる 50 の TipsAdvent Calendar 2023

Day 17

Nimで暗黙的に型変換する

Last updated at Posted at 2023-12-19

この記事はnim 1.6.14で動作を確認しています。

これは何?

NimをPythonと同じ感覚で使っていると、int * floatのような計算の時にintが自動的にfloatにならなくて、毎回float(int)と書くのが少しめんどくさいって思うことがある。ここら辺を暗黙的にやってくれるようにしたい。

結論

converterを定義してあげることで、暗黙の型変換が行われるようになる。

converter toFloat(x: int):float = float(x)

# 基本
var a = 30
var b = 2.5
echo a * b #75.0

#関数の呼び出し
proc test1(x:float)=
    echo x

test1(a) #30.0

#同名関数がある場合の優先度
proc test2(x:int)=
    echo "int"
proc test2(x:float)=
    echo "float"

test2(a) #int

#autoで引数が定義されている関数への優先度
proc test3(x:auto)=
    echo typeof(x)

test3(a) #int

proc test4(x:float)=
    echo "float!"
proc test4(x:auto)=
    echo typeof(x)

test4(a) #int

また、以下はコンパイルエラーとなる。

暗黙的に変換して実行できる同名関数が複数ある場合

converter toString(x: int):string = $x

proc test5(x:float)=
    echo "float"
proc test5(x:string)=
    echo "string"

var a = 30
test3(a) #コンパイルエラー。

Error: ambiguous call; both Main.test3(x: float) [proc declared in Main.nim(25, 6)] and Main.test3(x: string) [proc declared in Main.nim(27, 6)] match for: (int)

複数のコンバータを経由すれば型変換できるもの

var a = 30
converter toFloat(x: int):float = float(x)
converter toString(x: float):string = $x
proc test6(x:string)=
    echo "hello"

test4(a) #コンパイルエラー

Error: type mismatch: got <int>

もとから暗黙に変換されるもの

公式ドキュメントによると、以下の関数がtrueとなるような型aと型bは、型aから型bに暗黙的に変換される。

proc isImplicitlyConvertible(a, b: PType): bool =
  if isSubtype(a, b):
    return true
  if isIntLiteral(a):
    return b in {int8, int16, int32, int64, int, uint, uint8, uint16,
                 uint32, uint64, float32, float64}
  case a.kind
  of int:     result = b in {int32, int64}
  of int8:    result = b in {int16, int32, int64, int}
  of int16:   result = b in {int32, int64, int}
  of int32:   result = b in {int64, int}
  of uint:    result = b in {uint32, uint64}
  of uint8:   result = b in {uint16, uint32, uint64}
  of uint16:  result = b in {uint32, uint64}
  of uint32:  result = b in {uint64}
  of float32: result = b in {float64}
  of float64: result = b in {float32}
  of seq:
    result = b == openArray and typeEquals(a.baseType, b.baseType)
  of array:
    result = b == openArray and typeEquals(a.baseType, b.baseType)
    if a.baseType == char and a.indexType.rangeA == 0:
      result = b == cstring
  of cstring, ptr:
    result = b == pointer
  of string:
    result = b == cstring
  of proc:
    result = typeEquals(a, b) or compatibleParametersAndEffects(a, b)

まとめると、

  • 各種型は親タイプに変換できる
  • 整数リテラルはint,uint,float各種に変換できる
  • 各種int,uintはbit数が自分よりも大きい型に暗黙に変換できる
  • float32float64はそれぞれ暗黙に変換できる
  • seqarrayはその中身の型が一致しているopenArrayに変換できる
  • charのarrayはindexが0始まりのときcstringに変換できる
  • cstringptrpointerに変換できる
  • stringcstringに変換できる
  • procは引数の型が一致している場合は変換できる。compatibleParametersAndEffects(a, b)は現在使われていないようだ。

ところで、上のコードだとaがarray[char]でbがopenArray[char]のとき変換できなそうなのだが、普通にできるようだ。

参考・引用文献

Nim Manual Convertible relation

追記

Nimのstd/lenientopsを使えば、floatとintの演算は大体定義されていそうなので一番最初に上げた目的だけなら、そちらをimportする方が楽。
std/lenientops

2
1
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
2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?