una buona domanda.
Il codice di riferimento è in realtà considerato come una struttura di dati molto stilizzata. Quella struttura dati "sembra essere eseguibile". Ma devi capire come funziona.
Per esempio, da @ suggerimento di WiseGenius:
make-password: func[Length] [
chars: "QWERTYUIOPASDFGHJKLZXCVBNM1234567890"
password: copy ""
loop Length [append password (pick chars random Length)]
password
]
Date un'occhiata al blocco contenente append password...
.Quel blocco è "immaginato" lì; quello che sembra davvero come sotto il cofano è:
chars: **pointer to string! 0xSSSSSSS1**
password: copy **pointer to string! 0xSSSSSSS2**
loop Length **pointer to block! 0xBBBBBBBB**
password
Tutte le serie stanno lavorando in questo modo quando vengono caricati dall'interprete. Strings, blocchi, file binari, sentieri, parens, ecc Dato che si tratta di "tartarughe fino in fondo", se si seguono attraverso tale puntatore, il blocco 0xBBBBBBBB è internamente:
append password **pointer to paren! 0xPPPPPPPP**
Un risultato di questo è che una serie può fare riferimento (e quindi "ripreso") in più posti:
>> inner: [a]
>> outer: reduce [inner inner]
[[a] [a]]
>> append inner 'b
>> probe outer
[[a b] [a b]]
questo può essere fonte di confusione per i nuovi arrivati, ma una volta capito la struttura dei dati si comincia a sapere quando usare COPY.
Quindi hai notato un'interessante implicazione di questo con le funzioni. Considerate questo programma:
foo: func [] [
data: []
append data 'something
]
source foo
foo
foo
source foo
che produce un risultato forse-sorprendente:
foo: func [][
data: []
append data 'something
]
foo: func [][
data: [something something]
append data 'something
]
Noi chiamiamo foo
un paio di volte, sembra che codice sorgente della funzione sta cambiando, come lo facciamo. È, in un certo senso, codice auto-modificante.
Se questo ti infastidisce, ci sono strumenti in R3-Alpha per attaccarlo. Puoi usare PROTECT per proteggere i corpi delle funzioni dalle modifiche e persino creare le tue alternative alle routine come FUNC e FUNCTION che lo faranno per te. (?? PFUNC PFUNCTION) In Rebol versione 3 è possibile scrivere:
pfunc: func [spec [block!] body [block!]] [
make function! protect/deep copy/deep reduce [spec body]
]
foo: pfunc [] [
data: []
append data 'something
]
foo
Quando si esegue che si ottiene:
*** ERROR
** Script error: protected value or series - cannot modify
** Where: append foo try do either either either -apply-
** Near: append data 'something
In modo che le forze di copiare serie. Sottolinea inoltre che FUNC è solo una funzione! stesso, e così è FUNZIONE. Puoi creare i tuoi generatori.
Questo potrebbe spezzare il cervello e potresti correre urlando dicendo "questo non è un modo sensato di scrivere software". O forse dirai "mio Dio, è pieno di stelle". Le reazioni possono variare. Ma è abbastanza fondamentale per il "trucco" che alimenta il sistema e gli dà una flessibilità selvaggia.
(Nota: La Ren-C branch of Rebol3 ha fondamentalmente fatto in modo che gli enti di funzione - e serie source in generale - sono bloccati di default Se si vuole una variabile statica in una funzione, si può dire foo: func [x <static> accum (copy "")] [append accum x | return accum]
e la. la funzione si accumulerà stato in accum
tra le chiamate.)
sarò anche suggerire prestando particolare attenzione a ciò che sta realmente accadendo in ogni esecuzione. Prima di eseguire la funzione foo
, i dati non hanno alcun valore. Quello che succede è ogni volta che eseguiamo la funzione e il valutatore vede una SET-WORD! seguito da un valore di serie, esegue l'assegnazione alla variabile.
data: **pointer to block! 0xBBBBBBBB**
Dopo che l'assegnazione, avrete due riferimenti al blocco esistenti.Uno è la sua esistenza nella struttura del codice stabilita al momento LOAD, prima che la funzione fosse mai stata eseguita. Il secondo riferimento è quello che è stato memorizzato nella variabile dati. È attraverso questo secondo riferimento che stai modificando questa serie.
e notare che i dati verranno riassegnate ogni volta che la funzione viene eseguito. Ma riassegnato allo stesso valore più e più volte ... quel puntatore di blocco originale! Questo è il motivo per cui devi COPIARE se vuoi un nuovo blocco per ogni corsa.
Afferrare la semplicità sottostante nelle regole di valutazione è parte della vertiginosa interes- sione. Questo è il modo in cui la semplicità è stata vestita per creare un linguaggio (in un modo in cui potresti torcere a modo tuo). Per esempio, non v'è alcuna "multiple-assegnazione":
a: b: c: 10
Questo è solo il valutatore colpire un: come un set-PAROLA! simbolo e dicendo "okay, associamo la variabile a nel suo contesto vincolante con qualsiasi altra espressione produca.". b: fa lo stesso. c: fa lo stesso, ma colpisce un terminale a causa del valore intero 10 ... e quindi anche il valore di viene valutato a 10. Quindi sembra un'assegnazione multipla.
Quindi ricorda che l'istanza originale di una serie letterale è quella che si trova nella sorgente caricata. Se il valutatore riesce a fare questo tipo di SET-WORD! o SET assignment, prenderà a prestito il puntatore a quel letterale nella fonte per attirare la variabile. È un riferimento mutevole. Tu (o le astrazioni che disegni) puoi renderlo immutabile con PROTECT o PROTECT/DEEP, e puoi renderlo non-un-riferimento con COPY o COPY/DEEP.
nota correlata
Alcuni sostengono che non si dovrebbe mai scrivere copia [] ... perché (a) si potrebbe ottenere l'abitudine di dimenticare di scrivere il COPY, e (b) stai facendo una serie inutilizzata ogni volta che lo fai. Quel "modello di serie vuoto" viene assegnato, deve essere scansionato dal garbage collector e nessuno lo tocca mai.
Se si scrive fare blocco! 10 (o qualunque dimensione si voglia preassegnare il blocco) si evita il problema, si salva una serie e si offre un suggerimento per il dimensionamento.
@Joachim No, si comporta esattamente allo stesso modo. – Caridorc