Questa è una cosa molto comune in D. È come funzionano le gamme. Ad esempio, il tipo più semplice di gamma - gamma di ingresso - deve avere 3 funzioni:
bool empty(); //Whether the range is empty
T front(); // Get the first element in the range
void popFront(); //pop the first element off of the range
funzioni su modelli quindi utilizzare std.range.isInputRange
per verificare se un tipo è un intervallo valido. Per esempio, il sovraccarico di più fondamentale di std.algorithm.find
assomiglia
R find(alias pred = "a == b", R, E)(R haystack, E needle)
if (isInputRange!R &&
is(typeof(binaryFun!pred(haystack.front, needle)) : bool))
{ ... }
isInputRange!R
è true
se R
è un intervallo di input valido, e is(typeof(binaryFun!pred(haystack.front, needle)) : bool)
è true
se pred
accetta haystack.front
e needle
e restituisce un tipo che è implicitamente convertibile in bool
. Quindi, questo sovraccarico si basa interamente sulla tipizzazione anatra statica.
Per quanto riguarda isInputRange
sé, sembra qualcosa di simile
template isInputRange(R)
{
enum bool isInputRange = is(typeof(
{
R r = void; // can define a range object
if (r.empty) {} // can test for empty
r.popFront(); // can invoke popFront()
auto h = r.front; // can get the front of the range
}));
}
Si tratta di un modello omonimo, in modo da quando viene utilizzato, esso viene sostituito con il simbolo con il suo nome, che in questo caso è un enum di tipo bool
. E che bool
è true
se il tipo dell'espressione non è void
. typeof(x)
risultati in void
se l'espressione non è valida; in caso contrario, è il tipo di espressione x
. E is(y)
risultati in true
se non è void
. Quindi, isInputRange
finirà per essere true
se il codice nell'espressione typeof
viene compilato e false
in caso contrario.
L'espressione nella isInputRange
verifica che è possibile dichiarare una variabile di tipo R
, che R
ha un membro (sia esso una funzione, variabile o qualsiasi altra cosa) di nome empty
che può essere utilizzato in una condizione, che R
ha una funzione chiamato popFront
che non accetta argomenti e che R
ha un membro front
che restituisce un valore. Questa è l'API prevista per un intervallo di input e l'espressione all'interno di typeof
verrà compilata se l'API R
segue tale API e, pertanto, true
per tale tipo sarà true
. Altrimenti, sarà false
.
La libreria standard di D contiene alcuni di questi modelli omonimi (in genere chiamati tratti) e ne fa un uso pesante nei vincoli del modello. std.traits
in particolare ne ha parecchi. Quindi, se vuoi altri esempi di come sono scritti tali tratti, puoi guardare lì (anche se alcuni di essi sono abbastanza complicati). Gli interni di tali tratti non sono sempre particolarmente belli, ma incapsulano bene i test di battitura delle anatre in modo che i vincoli dei template siano molto più puliti e comprensibili (sarebbero molto, molto più brutti se tali test fossero inseriti direttamente in essi).
Quindi, questo è il normale approccio per la digitazione statica di anatra in D. Ci vuole un po 'di pratica per capire come scriverli bene, ma questo è il modo standard per farlo e funziona. Ci sono state persone che hanno suggerito di provare a proporre qualcosa di simile al tuo suggerimento Implements!(S, Interface)
, ma non è ancora arrivato nulla di simile, e un tale approccio sarebbe in realtà meno flessibile, rendendolo inadatto per molti tratti (anche se potrebbe certamente essere fatto per funzionare con quelli di base). Indipendentemente da ciò, l'approccio che ho descritto qui è attualmente il modo standard per farlo.
Inoltre, se non si conoscono molto gli intervalli, suggerirei di leggere this.
Stai cercando di creare un'unica funzione per gestire tutti i casi d'uso? Penso che ciò richiederebbe [static foreach] (http://d.puremagic.com/issues/show_bug.cgi?id=4085), ma non ne sono sicuro. Forse un po 'di magia CTFE funzionerebbe? Anche di interesse: http://www.digitalmars.com/d/archives/digitalmars/D/static_foreach_108369.html – tjameson
@tjameson Sì, credo che per l'esempio precedente dovresti scorrere tutti i metodi definiti nella tua interfaccia 'struct in fase di compilazione. Tuttavia, sono aperto anche ad altri modi per raggiungere lo stesso obiettivo, se esistono. – Dan
D ha wrap e unwrap (http://dlang.org/phobos-prerelease/std_typecons.html#.wrap e http://dlang.org/phobos-prerelease/std_typecons.html#.unwrap) fornisce funzionalità simili a quelle di Go digitando anatra. – DejanLekic