4

以下のMercuryコードをコンパイルすると、コンパイラから次のエラーが発生します。

In clause for `main(di, uo)':
  in argument 1 of call to predicate
  `test_with_anonymous_functions.assert_equals'/5:
  mode error: variable `V_15' has
  instantiatedness `/* unique */((func) =
  (free >> ground) is semidet)',
  expected instantiatedness was `((func) =
  (free >> ground) is det)'.

コンパイラが言っているのは、「型を宣言したときtest_case、決定論を指定しなかったので、意味があると思いましdetた。しかし、semidetラムダを渡した」と思います。

私の質問:

  • 型の決定論を宣言するための構文は何ですか?私が試した推測はすべて構文エラーを引き起こしました。
  • 誰かがのインスタンス化の/* unique */部分が何を意味するのか説明できますか?TestCaseそれは、与えられたインスタンス化と期待されるインスタンス化の間に不一致を引き起こしますか?
  • でラムダを宣言するためのより冗長でない方法はありmainますか?ラムダ内のコードと同じくらい、ラムダに関する宣言があります。

コード:

% (Boilerplate statements at the top are omitted.)

% Return the nth item of a list
:- func nth(list(T), int) = T.
:- mode nth(in,      in)  = out is semidet.
nth([Hd | Tl], N) = (if N = 0 then Hd else nth(Tl, N - 1)).

% Unit testing: Execute TestCase to get the 
% actual value. Print a message if (a) the lambda fails
% or (b) the actual value isn't the expected value.
:- type test_case(T) == ((func) = T).
:- pred assert_equals(test_case(T), T,  string, io.state, io.state).
:- mode assert_equals(in,           in, in,     di,       uo) is det.
assert_equals(TestCase, Expected, Message, !IO) :-
    if   Actual = apply(TestCase), Actual = Expected
    then true % test passed. do nothing.
    else io.format("Fail:\t%s\n", [s(Message)], !IO).

main(!IO) :-
    List = [1, 2, 3, 4],
    assert_equals( ((func) = (nth(List, 0)::out) is semidet),
                 1, "Nth", !IO).
4

3 に答える 3

4

ベンは正しい、

Mercuryは、関数がデフォルトで決定論的であると想定していることを付け加えたいと思います(関数は決定論的であるべきであることが賢明です)。これは、決定論を宣言する必要がある述語には当てはまりません。これにより、ボイラープレートが少ないという理由だけで、他の関数や述語よりも決定論的な関数を使用して高次のプログラミングを簡単に行うことができます。

このため、ラムダ式をもう少し簡潔にすることができます。ヘッドの変数Sをボディに置き換えることで、ラムダ式のボディをヘッドに移動することもできます。

apply_transformer((func(S0) = "Hello " ++ S0),
                  "World", Msg),
于 2011-09-12T10:48:11.217 に答える
3

これもコツをつかむのに少し時間がかかりました。

問題は、高次の項の最頻値がそのタイプの一部ではないことです。したがって、型の決定論を宣言するための構文はありません。高次項の決定論は、モードで実行されます。

あなたの例では、の最初の引数assert_equalsはタイプtest_case(T)がですが、モードはですin。これは、関数semidetが失われるという事実が失われることを意味します。det渡した関数が;だった場合、実際にコンパイルまたは実行されるかどうかはわかりません。その場合でも、モードは実際にはすべきではありませんin

次に例を示します。

:- pred apply_transformer(func(T) = T, T, T).
:- mode apply_transformer(func(in) = out is det, in, out).
apply_transformer(F, X0, X) :-
    X = F(X0).

main(!IO) :-
    apply_transformer((func(S0) = S is det :- S = "Hello " ++ S0),
                      "World", Msg),
    print(Msg, !IO),
    nl(!IO).

ご覧のとおり、最初の引数の型は、apply_transformerそれが高階関数であることを示しているだけで、1つの引数を取り、同じ型の結果を返します。in関数パラメーターにモードがあり、関数結果にモードoutがあり、その決定論がであると実際に言うのはモード宣言ですdet

/*unique */エラーメッセージの一部は、コンパイラがそれを一意の値と見なしていることを示していると思います。io通常の状態以外で独自のモードを使用していないため、それが問題かどうかはわかりません。

ラムダ構文に関しては、残念ながらこれ以上のことはできないと思います。Mercuryのラムダの構文はかなり不十分だと思います。それらは非常に冗長なので、私は通常、最も些細なラムダを除くすべての代わりに、名前付き関数/述語を作成することになります。



于 2011-09-12T00:23:35.397 に答える
2

2番目の質問に答えるため/* unique */に、エラーメッセージのは、への呼び出しの最初の引数を参照していますassert_equals。これは、作成したラムダ項です。これは、この用語が使用される唯一の場所であるため、この用語への参照は、呼び出しの時点で一意です。

一意のインスタンスは地上のインスタンスと一致します(ただし、その逆はありません)。したがって、この場合、一意性によって不一致が発生することはありません。問題は決定論です。

于 2014-10-05T14:01:09.930 に答える