英語版
このページの英語版を見る
std.sumtype
SumTypeは
汎用の差別化共用体の実装である。
デザイン・バイ・イントロスペクションを用いて安全で効率的なコードを生成する。その特徴
を含む:
パターン・マッチ。
- 自己参照型のサポート。
- 完全な属性の正しさ(pure,@safe,@nogc,nothrow は可能な限り推論される。 は可能な限り推論される)。
- DIP 1000と互換性のある型安全かつメモリ安全なAPI (scope)。
- 実行時の型情報に依存しない(TypeInfo)。
- BetterCとの互換性。
License:
Boost License 1.0
Authors:
Paul Backus
ソース std/sumtype.d
Examples:
基本的な使い方
import std.math.operations : isClose; struct Fahrenheit { double degrees; } struct Celsius { double degrees; } struct Kelvin { double degrees; } alias Temperature = SumType!(Fahrenheit, Celsius, Kelvin); // いずれかのメンバー型から構築する。 Temperature t1 = Fahrenheit(98.6); Temperature t2 = Celsius(100); Temperature t3 = Kelvin(273); // パターンマッチングを使用して値にアクセスする。 Fahrenheit toFahrenheit(Temperature t) { return Fahrenheit( t.match!( (Fahrenheit f) => f.degrees, (Celsius c) => c.degrees * 9.0/5 + 32, (Kelvin k) => k.degrees * 9.0/5 - 459.4 ) ); } assert(toFahrenheit(t1).degrees.isClose(98.6)); assert(toFahrenheit(t2).degrees.isClose(212)); assert(toFahrenheit(t3).degrees.isClose(32)); // refを使用して、その場で値を変更する。 void freeze(ref Temperature t) { t.match!( (ref Fahrenheit f) => f.degrees = 32, (ref Celsius c) => c.degrees = 0, (ref Kelvin k) => k.degrees = 273 ); } freeze(t1); assert(toFahrenheit(t1).degrees.isClose(32)); // デフォルトの結果を返すには、catch-allハンドラを使用する。 bool isFahrenheit(Temperature t) { return t.match!( (Fahrenheit f) => true, _ => false ); } assert(isFahrenheit(t1)); assert(!isFahrenheit(t2)); assert(!isFahrenheit(t3));
Examples:
以下のlength とhoriz 関数の中で、match のハンドラは引数の型を指定していない。
は引数の型を指定しない。その代わり、マッチングは
とプロパティを持つ型はすべて、x ハンドラによってマッチングされる。y
プロパティを持つ型は、rect ハンドラーによってマッチングされ、r と
theta プロパティを持つ型はpolar ハンドラーによってマッチングされる。
イントロスペクションに基づくマッチング
import std.math.operations : isClose; import std.math.trigonometry : cos; import std.math.constants : PI; import std.math.algebraic : sqrt; struct Rectangular { double x, y; } struct Polar { double r, theta; } alias Vector = SumType!(Rectangular, Polar); double length(Vector v) { return v.match!( rect => sqrt(rect.x^^2 + rect.y^^2), polar => polar.r ); } double horiz(Vector v) { return v.match!( rect => rect.x, polar => polar.r * cos(polar.theta) ); } Vector u = Rectangular(1, 1); Vector v = Polar(1, PI/4); assert(length(u).isClose(sqrt(2.0))); assert(length(v).isClose(1)); assert(horiz(u).isClose(1)); assert(horiz(v).isClose(sqrt(0.5)));
Examples:
この例では、特別なプレースホルダ型This を使って、再帰データ型である
を定義している。
抽象構文木
を定義している。
算術式評価器
import std.functional : partial; import std.traits : EnumMembers; import std.typecons : Tuple; enum Op : string { Plus = "+", Minus = "-", Times = "*", Div = "/" } // 式は以下のいずれかである // - 数値、 // - 変数、または // - 2つの部分式を組み合わせた2項演算子。 alias Expr = SumType!( double, string, Tuple!(Op, "op", This*, "lhs", This*, "rhs") ); // Tuple!(Op, "op", Expr*, "lhs", Expr*, "rhs")の短縮形であり、 // これは、上のTuple型でExprがThisに置き換えられたものである。 alias BinOp = Expr.Types[2]; // 数値式用のファクトリー関数 Expr* num(double value) { return new Expr(value); } // 変数式のファクトリー関数 Expr* var(string name) { return new Expr(name); } // 二項演算式のファクトリー関数 Expr* binOp(Op op, Expr* lhs, Expr* rhs) { return new Expr(BinOp(op, lhs, rhs)); } // BinOp式を作成するための簡易ラッパー alias sum = partial!(binOp, Op.Plus); alias diff = partial!(binOp, Op.Minus); alias prod = partial!(binOp, Op.Times); alias quot = partial!(binOp, Op.Div); // exprを評価し、envの変数を参照する double eval(Expr expr, double[string] env) { return expr.match!( (double num) => num, (string var) => env[var], (BinOp bop) { double lhs = eval(*bop.lhs, env); double rhs = eval(*bop.rhs, env); final switch (bop.op) { static foreach (op; EnumMembers!Op) { case op: return mixin("lhs" ~ op ~ "rhs"); } } } ); } // exprの"きれいに表示された"表現を返す string pprint(Expr expr) { import std.format : format; return expr.match!( (double num) => "%g".format(num), (string var) => var, (BinOp bop) => "(%s %s %s)".format( pprint(*bop.lhs), cast(string) bop.op, pprint(*bop.rhs) ) ); } Expr* myExpr = sum(var("a"), prod(num(2), var("b"))); double[string] myEnv = ["a":3, "b":4, "c":7]; writeln(eval(*myExpr, myEnv)); // 11 writeln(pprint(*myExpr)); // "(a + (2 * b))"
- struct
This
; SumType
を囲むプレースホルダ。- struct
SumType
(Types...) if (is(NoDuplicates!Types == Types) && (Types.length > 0)); - タグ付き共用体。 タグ付き結合で、指定された型のいずれかの値を保持することができる。の値は、 パターンマッチングを使用して操作できる。
SumType
の値は、パターンマッチを使って
操作できる。 曖昧さを避けるため、型の重複は許されない。 「を参照のこと)。 特殊型This は、 と同様に、自己参照型を作成するためのプレースホルダとして使用できる。 Algebraic と同様に、自己参照型を作成するためのプレースホルダとして使用できる。以下を参照のこと。 "算術式評価子"の例を参照のこと。 を参照のこと。 ASumType
はデフォルトで初期化され、その最初のメンバ型の".init "値を保持する。 Aは、通常の共用体のように、その最初のメンバ型の値を保持するように初期化される。バージョン識別子 SumTypeNoDefaultCtor を使うと、この動作を無効にすることができる。See Also:- alias
Types
= AliasSeq!(ReplaceTypeUnless!(isSumTypeInstance, This, typeof(this), TemplateArgsOf!SumType)); - SumType が保持できる型。
- this(T
value
);
const this(const(T)value
);
immutable this(immutable(T)value
);
inout this(Value)(Valuevalue
)
if (is(Value == DeducedParameterType!(inout(T)))); - 特定の値を保持するSumType を構築する。
- inout this(ref inout(SumType)
other
); - 別のSumType のコピーであるSumType を構築する。
- ref SumType
opAssign
(Trhs
); - SumType に値を代入する。値をSumType に代入する。 代入されるメンバ以外のメンバにポインタや参照が含まれていると、代入によってメモリ破壊が発生する可能性がある。 がメモリ破壊を引き起こす可能性がある。 以下の"メモリ破壊"の例を参照のこと)。 を参照のこと)。したがって、このような代入は @system. 個々の代入は、呼び出し元が以下のことを保証できる場合、@trusted 。 呼び出し元が、ポインタや参照を含むメンバへの未解決の参照がないことを保証できる場合、 個別の代入は。SumType 個々の代入は、呼び出し元が、代入が発生した時点でポインタや参照を含むメンバへの未解決の参照がないことを保証できる場合、。 個々の代入は、呼び出し元が、代入が発生した時点で、ポインタや参照を含むメンバへの未処理の参照がないことを保証できる場合、。Examples:この例は、SumType への代入がどのように使われるかを示している。 @system 。@safe 。 s = 123 。
メモリ破損
SumType!(int*, int) s = new int; s.tryMatch!( (ref int* p) { s = 123; // `p`を上書きする return *p; // 未定義の動作 } );
- ref SumType
opAssign
(ref SumTyperhs
); - 別のSumType の値をこの値にコピーする。@safetyの詳細については、value-assignmentオーバーロードを参照のこと。 コピー代入は、Types のいずれかがコピー不可の場合、@disabled となる。
- ref SumType
opAssign
(SumTyperhs
); - 別のSumType の値をこの値に移動する。@safetyの詳細についてはvalue-assignmentオーバーロードを参照のこと。
- bool
opEquals
(this This, Rhs)(auto ref Rhsrhs
)
if (!is(CommonType!(This, Rhs) == void)); - 2つのSumTypesが等しいかどうかを比較する。2つのSumTypeが等しいのは、それらが同じ種類のSumType で、同じ型の値を含み、それらの値が等しい場合である。 が同じ型の値を含み、それらの値が等しい場合に等しい。
- string
toString
(this This)(); - SumType の現在値の文字列表現を返す。-betterC と一緒にコンパイルされた場合は利用できない。
- void
toString
(this This, Sink, Char)(ref Sinksink
, const ref FormatSpec!Charfmt
); - SumType の現在値の書式付き書き込みを処理する。-betterC と一緒にコンパイルした場合は使用できない。Parameters:
Sink sink
書き込む出力範囲。 FormatSpec!Char fmt
使用するフォーマット指定子。 See Also: - const size_t
toHash
(); - SumType の現在値のハッシュを返す。-betterC と一緒にコンパイルされた場合は利用できない。
- enum bool
isSumType
(T); - Examples:
static struct ConvertsToSumType { SumType!int payload; alias payload this; } static struct ContainsSumType { SumType!int payload; } assert(isSumType!(SumType!int)); assert(isSumType!ConvertsToSumType); assert(!isSumType!ContainsSumType);
- template
match
(handlers...) SumTypeに
保持された値で、型に応じた関数を呼び出す。SumTypeが
持ちうるそれぞれの型について、指定されたハンドラがその型の引数を1つ受け入れるかどうかが順番にチェックされる。 は、その型の単一の引数を受け付けるかどうかを順番にチェックする。 その型にマッチする最初のものが選ばれる。(注釈:) 最初にマッチしたものが、必ずしも完全にマッチするとは限らないことに注意。 よくある落とし穴については、"意図しないマッチを避ける"を参照のこと)。 を参照のこと)。 すべての型はマッチするハンドラーを持たなければならず、すべてのハンドラーは少なくとも一つの型にマッチしなければならない。 これはコンパイル時に強制される。これはコンパイル時に強制される。 ハンドラーは、関数、デリゲート、またはopCall オーバーロードを持つオブジェクトである。もし 複数のオーバーロードを持つ "関数"がハンドラーとして与えられた場合、すべてのオーバーロードがマッチする可能性があるとみなされる。 がマッチする可能性がある。 テンプレート化されたハンドラも受け付ける。 にマッチする。以下を参照のこと。 「を参照のこと。 テンプレート化されたハンドラの使用例 "を参照のこと。 マッチングのために複数のSumTypesが
渡された場合、それらの値は別々の引数としてハンドラに渡される。 マッチングに複数のSumTypesが渡された場合、それらの値は別々の引数としてハンドラに渡され、マッチングは可能な値型の組み合わせごとに行われる。 マッチングが行われる。例については"複数のディスパッチ"を参照のこと。 例 "を参照のこと。Returns:現在保持されている型にマッチするハンドラーから返される値。See Also:Examples:時には、暗黙の変換によって、ハンドラが意図した以上の型にマッチしてしまうことがある。 にマッチしてしまうことがある。以下の例は、この問題に対する2つの解決策を示している。不用意なマッチングを避ける
alias Number = SumType!(double, int); Number x; // 問題: intは暗黙的にdoubleに変換されるため、 // 両方の型に対してdoubleハンドラが使用され、intハンドラは決して一致しない。 assert(!__traits(compiles, x.match!( (double d) => "got double", (int n) => "got int" ) )); // 解決策1: "より専門的"な型(この場合はint型)のハンドラーを、 // 変換先の型のハンドラの前に置く。 assert(__traits(compiles, x.match!( (int n) => "got int", (double d) => "got double" ) )); // 解決策2: 暗黙的に変換されるあらゆる型ではなく、 // 一致するはずの正確な型のみを受け入れるテンプレートを使用する。 alias exactly(T, alias fun) = function (arg) { static assert(is(typeof(arg) == T)); return fun(arg); }; // ここで、たとえdoubleハンドラを最初に配置したとしても、 // それはdoubleに対してのみ使用され、intには使用されない。 assert(__traits(compiles, x.match!( exactly!(double, d => "got double"), exactly!(int, n => "got int") ) ));
Examples:パターン・マッチは、複数の引数を持つハンドラを渡すことで、一度に複数のSumType。 ハンドラに複数の引数を 渡すことで、一度に複数のハンドラに対してパターンマッチングを行うことができる。これは通常 をネストして使うよりも、より簡潔なコードになる。複数の派遣
match
をネストして呼び出すよりも簡潔なコードになる。struct Point2D { double x, y; } struct Point3D { double x, y, z; } alias Point = SumType!(Point2D, Point3D); version (none) { // この関数は機能するが、コードは醜く、繰り返しが多い。 // マッチするために3つの別々の呼び出しを使っている! @safe pure nothrow @nogc bool sameDimensions(Point p1, Point p2) { return p1.match!( (Point2D _) => p2.match!( (Point2D _) => true, _ => false ), (Point3D _) => p2.match!( (Point3D _) => true, _ => false ) ); } } // このバージョンはずっといい。 @safe pure nothrow @nogc bool sameDimensions(Point p1, Point p2) { alias doMatch = match!( (Point2D _1, Point2D _2) => true, (Point3D _1, Point3D _2) => true, (_1, _2) => false ); return doMatch(p1, p2); } Point a = Point2D(1, 2); Point b = Point2D(3, 4); Point c = Point3D(5, 6, 7); Point d = Point3D(8, 9, 0); assert( sameDimensions(a, b)); assert( sameDimensions(c, d)); assert(!sameDimensions(a, c)); assert(!sameDimensions(d, b));
- ref auto
match
(SumTypes...)(auto ref SumTypesargs
)
if (allSatisfy!(isSumType, SumTypes) && (args
.length > 0)); - 実際の
match
関数」である。Parameters:SumTypes args
1つ以上の SumType
オブジェクト。
- template
tryMatch
(handlers...) - に保持された値で、型に応じた関数を呼び出そうとする。
に
保持された値で型に応じた関数を呼び出そうとし、失敗するとスローする。マッチはmatchと
同じルールで選択されるが、網羅的である必要はない。 言い換えると、型(または型の組み合わせ)は、マッチするハンドラを持たないことが許される。 がマッチするハンドラを持たないことも許される。実行時にハンドラのない型に遭遇した場合、 Matchが実行される。 実行時にハンドラのない型に遭遇した場合、MatchExceptionがスローされる。 -betterC でコンパイルされた場合は利用できない。
Returns:現在保持されている型にマッチするハンドラーから返される値、 その型に対してハンドラーが与えられていれば、そのハンドラーから返される値。See Also:- ref auto
tryMatch
(SumTypes...)(auto ref SumTypesargs
)
if (allSatisfy!(isSumType, SumTypes) && (args
.length > 0)); - 実際の
tryMatch
関数である。Parameters:SumTypes args
1つ以上の SumType
オブジェクト。
- class
MatchException
: object.Exception; - 未処理の型に遭遇したときに
tryMatch
によってスローされる。-betterC と共にコンパイルされた場合は利用できない。- pure nothrow @nogc @safe this(string
msg
, stringfile
= __FILE__, size_tline
= __LINE__);
- template
canMatch
(alias handler, Ts...) if (Ts.length > 0) - handler がTs にマッチする可能性があれば真、そうでなければ偽。マッチがどのように選択されるかについての完全な説明は、
matchの
ドキュメントを参照のこと。 のドキュメントを参照のこと。Examples:alias handleInt = (int i) => "got an int"; assert( canMatch!(handleInt, int)); assert(!canMatch!(handleInt, string));
Copyright © 1999-2024 by the D Language Foundation
DEEPL APIにより翻訳、ところどころ修正。
このページの最新版(英語)
このページの原文(英語)
翻訳時のdmdのバージョン: 2.108.0
ドキュメントのdmdのバージョン: 2.109.1
翻訳日付 :
HTML生成日時:
編集者: dokutoku