Sulkeuma ohjelmoinnissa — määritelmä, toiminta ja esimerkit

Sulkeuma ohjelmoinnissa: selkeä määritelmä, sulkeuman toiminta ja käytännön esimerkit koodilla — opi käyttämään sulkeumia tehokkaasti ja ymmärrä niiden ympäristö.

Tekijä: Leandro Alegsa

Tietojenkäsittelytieteessä sulku on funktio, jolla on oma ympäristö. Tässä ympäristössä on vähintään yksi sidottu muuttuja (nimi, jolla on arvo, esimerkiksi luku). Sulkeuman ympäristö pitää sidotut muuttujat muistissa sulkeuman käyttökertojen välillä.

Peter J. Landin antoi tälle ajatukselle nimen sulkeminen vuonna 1964. Scheme-ohjelmointikieli teki sulkemisista suosittuja vuoden 1975 jälkeen. Monissa sen jälkeen tehdyissä ohjelmointikielissä on sulkeumia.

Anonyymejä funktioita (funktioita, joilla ei ole nimeä) kutsutaan joskus virheellisesti sulkeumiksi. Useimmissa kielissä, joissa on anonyymejä funktioita, on myös sulkeumia. Anonyymi funktio on myös sulkeuma, jos sillä on oma ympäristö, jossa on vähintään yksi sidottu muuttuja. Anonyymi funktio, jolla ei ole omaa ympäristöä, ei ole sulkeuma. Nimetty sulkeuma ei ole nimetön.

Määritelmä ja toimintaperiaate

Sulkeuma yhdistää funktion ja sen ympäristön: se sisältää funktion koodin ja viittauksen ympäristöön, jossa funktio luotiin. Tämä ympäristö sisältää sidotut muuttujat (lokalit), joita funktio voi käyttää ja muuttaa myös sen jälkeen, kun alkuperäinen konteksti (esimerkiksi funktiokutsu) on palannut. Sulkeumat perustuvat yleensä leksikaaliseen (static) nimeämiskontekstiin — muuttujan merkitys määräytyy ohjelmakoodin rakenteen mukaan, ei ajon aikana muuttuvan kutsupinon mukaan.

Yksinkertainen esimerkki (JavaScript)

// tehtaan avulla tehdään laskuri, joka muistaa kutsukertojen määrän function makeCounter() {   let count = 0;   return function() {     count += 1;     return count;   }; }  const c = makeCounter(); console.log(c()); // 1 console.log(c()); // 2 

Tässä count elää sulkeuman ympäristössä ja sitä ei voi muuttaa suoraan ulkopuolelta — ainoa tapa on kutsua palautettua funktiota.

Esimerkki (Python)

def make_counter():     count = 0     def counter():         nonlocal count         count += 1         return count     return counter  c = make_counter() print(c())  # 1 print(c())  # 2 

Scheme-esimerkki

(define (make-counter)   (let ((count 0))     (lambda ()       (set! count (+ count 1))       count))) 

Käyttötapaukset

  • Tietojen kapselointi: sulkeumia käytetään tarjoamaan yksityinen tila julkisten rajapintojen taakse (esim. olioiden yksinkertainen korvaaja ilman luokkamekanismeja).
  • Takaisinsoitot ja tapahtumankäsittelijät: sulkeumat muistavat tarvittavat kontekstiarvot, vaikka tapahtuma käsiteltäisiin myöhemmin.
  • Osittainen sovellus ja currytys: sulkeumia käytetään sitomaan osan funktiolle annettavista argumenteista.
  • Iteraattorit ja generaattorit: tila säilyy kutsujen välillä sulkeuman avulla.

Toteutus ja muistinhallinta

Sulkeuman toteutus vaatii, että sulkeuman ympäristö säilyy elossa niin kauan kuin sulkeumaa itse käytetään. Käytännössä tämä tarkoittaa usein sitä, että muuttujat, jotka normaalisti olisivat pinolla, sijoitetaan heap-muistiin ja niitä hallitaan roskienkerääjän tai vastaavan avulla.

Kielissä ja kääntäjissä voidaan käyttää optimointeja kuten lambda-lifting (muuttujien muuttaminen funktion parametriksi) tai escape-analyysi (päätellään, tarvitseeko muuttuja jäädä heapille vai voidaan sijoittaa pinoon). Eri kielet voivat myös erilailla kopioida tai viitata suljetuille arvoille (esim. kopioida arvon vs. käyttää viittausta), mikä vaikuttaa käyttäytymiseen.

Huomioitavaa ja yleiset sudenkuopat

  • Muistin pidättäminen: sulkeuma voi pitää suuria tai odottamattomia rakenteita elossa, mikä voi johtaa muistinkäytön kasvuun, jos ei ole varovainen.
  • Loop-ongelmat: joissain kielissä, erityisesti ennen blokkiskoopin (let/const) laajaa käyttöä JavaScriptissä, silmukan sisällä luotu sulkeuma saattoi viitata samaan muuttujaan jokaisessa iteraatiossa, mikä johti odottamattomiin tuloksiin.
  • Debuggaus: sulkeumat voivat tehdä tilan seuraamisen vaikeammaksi, koska muuttujat elävät funktion eliniän ulkopuolella.
  • Syklit ja roskienkeruu: vanhemmissa järjestelmissä, joissa ei ole automaattista roskienkeräystä, sulkeumat saattoivat aiheuttaa muistivuotoja, jos niissä syntyi viitesyklejä.

Erot anonyymien funktioiden ja sulkeumien välillä

Anonyymi funktio on funktio, jolla ei ole nimeä. Sulkeuma on funktio, jolla on oma ympäristö, jossa on sidottuja muuttujia. Näin ollen anonyymi funktio voi olla sulkeuma (jos se sieppaa ympäristön), mutta se ei aina ole. Samoin nimetty funktio voi muodostaa sulkeuman, jos sen luontipaikalla on sidottuja muuttujia.

Yhteenveto: sulkeumat ovat voimakas ja yleinen abstraktio monissa ohjelmointikielissä: ne mahdollistavat funktion sisäisen tilan säilyttämisen kutsujen välillä, auttavat kapseloinnissa ja tukevat funktionaalisia ohjelmointitekniikoita, mutta vaativat myös ymmärrystä muistinhallinnasta ja mahdollisista sudenkuopista.

Sulkeminen ja ensimmäisen luokan funktiot

Arvot voivat olla numeroita tai muunlaisia tietoja, kuten kirjaimia, tai yksinkertaisemmista osista koostuvia tietorakenteita. Ohjelmointikielen säännöissä ensimmäisen luokan arvot ovat arvoja, jotka voidaan antaa funktioille, palauttaa funktioilla ja sitoa muuttujan nimeen. Funktioita, jotka ottavat tai palauttavat muita funktioita, kutsutaan korkeamman luokan funktioiksi. Useimmissa kielissä, joissa funktiot ovat ensimmäisen luokan arvoja, on myös ylemmän luokan funktioita ja sulkeumia.

Katso esimerkiksi seuraavaa Scheme-funktiota:

; Palauta luettelo kaikista kirjoista, joita on myyty vähintään KOLME kappaletta. (define (best-selling-books threshold) (filter (lambda (book) (>= (book-sales book) threshold)) book-list)))

Tässä esimerkissä lambda-lauseke (lambda (kirja) (>= (kirjamyynti kirja) kynnysarvo)) on osa funktiota myydyimmät-kirjat. Kun funktio ajetaan, Schemen on tehtävä lambda-lausekkeen arvo. Se tekee tämän tekemällä sulkeuman, jossa on lambdan koodi ja viittaus muuttujaan threshold, joka on vapaa muuttuja lambdan sisällä. (Vapaa muuttuja on nimi, jota ei ole sidottu arvoon.)

Tämän jälkeen suodatustoiminto suorittaa sulkemisen jokaiselle luettelossa olevalle kirjalle ja valitsee palautettavat kirjat. Koska sulkemisella itsellään on viittaus kynnysarvoon, sulkeminen voi käyttää tätä arvoa joka kerta, kun filter ajaa sulkemisen. Itse suodatinfunktio voidaan kirjoittaa täysin erilliseen tiedostoon.

Tässä on sama esimerkki kirjoitettuna uudelleen ECMAScriptillä (JavaScript), joka on toinen suosittu kieli, joka tukee sulkeumia:

// Palauta lista kaikista kirjoista, joita on myyty vähintään 'kynnysarvo'. function bestSellingBooks(kynnysarvo) { return bookList. filter( function(kirja) { return kirja. myynti >= kynnysarvo; }     ); }

ECMAScript käyttää tässä sanaa function lambdan sijasta ja Array.filter-metodia filter-funktion sijasta, mutta muuten koodi tekee saman asian samalla tavalla.

Funktio voi luoda sulkeuman ja palauttaa sen. Seuraava esimerkki on funktio, joka palauttaa funktion.

Järjestelmässä:

; Palauta funktio, joka approksimoi f:n derivaatan ; käyttäen dx:n väliä, jonka tulisi olla sopivan pieni. (define (derivative f dx) (lambda (x) (/ (- (f (+ x dx))) (f x))) dx)))))

ECMAScript:

// Palauta funktio, joka approksimoi f:n derivaatan // käyttäen dx:n väliä, jonka tulisi olla sopivan pieni. function derivative(f, dx) { return function(x) { return (f(x + dx) - f(x)) / dx; }; }

Sulkuympäristö säilyttää sidotut muuttujat f ja dx sen jälkeen, kun sulkeva funktio (derivaatta) palaa. Kielissä, joissa ei ole sulkeumia, nämä arvot menetettäisiin sen jälkeen, kun sulkeutuva funktio palaa. Kielissä, joissa on sulkeutumia, sidottu muuttuja on pidettävä muistissa niin kauan kuin jokin sulkeuma on sen sisällä.

Sulkeumaa ei tarvitse muodostaa käyttämällä anonyymiä funktiota. Esimerkiksi Python-ohjelmointikielessä on rajoitettu tuki anonyymeille funktioille, mutta siinä on sulkeumia. Yksi tapa, jolla yllä oleva ECMAScript-esimerkki voitaisiin toteuttaa Pythonissa, on esimerkiksi seuraava:

# Palauta funktio, joka approksimoi f:n derivaatan # käyttäen dx-väliä, jonka pitäisi olla sopivan pieni. def derivaatta(f, dx): def gradient(x): return (f(x + dx) - f(x)) / dx return gradientti

Tässä esimerkissä gradientti-niminen funktio muodostaa sulkeuman yhdessä muuttujien f ja dx kanssa. Ulompi sulkeva funktio nimeltä derivaatta palauttaa tämän sulkeuman. Tässä tapauksessa anonyymi funktio toimisi myös.

def derivative(f, dx): return lambda x: (f(x + dx) - f(x)) / dx

Pythonissa joudutaan usein käyttämään nimettyjä funktioita, koska sen lambda-lausekkeet voivat sisältää vain muita lausekkeita (koodia, joka palauttaa arvon) eikä lausekkeita (koodia, jolla on vaikutuksia mutta ei arvoa). Muissa kielissä, kuten Scheme-kielessä, kaikki koodi palauttaa arvon; Scheme-kielessä kaikki on lausekkeita.

Sulkujen käyttö

Sulkimilla on monia käyttötarkoituksia:

  • Ohjelmistokirjastojen suunnittelijat voivat antaa käyttäjille mahdollisuuden mukauttaa käyttäytymistään siirtämällä sulkuja argumentteina tärkeille funktioille. Esimerkiksi funktio, joka lajittelee arvoja, voi hyväksyä sulkuargumentin, joka vertaa lajiteltavia arvoja käyttäjän määrittelemän kriteerin mukaan.
  • Koska sulkeumat viivästyttävät evaluointia - eli ne eivät "tee" mitään ennen kuin niitä kutsutaan - niitä voidaan käyttää kontrollirakenteiden määrittelyyn. Esimerkiksi kaikki Smalltalkin vakio-ohjausrakenteet, mukaan lukien haarautuminen (if/then/else) ja silmukat (while ja for), määritellään käyttäen objekteja, joiden metodit hyväksyvät sulkeutumia. Käyttäjät voivat myös helposti määritellä omia ohjausrakenteita.
  • Voidaan tuottaa useita funktioita, jotka sulkevat saman ympäristön toisiinsa, jolloin ne voivat kommunikoida yksityisesti muuttamalla ympäristöä (kielillä, jotka sallivat määrityksen).

Järjestelmässä

(define foo #f) (define bar #f) (let ((secret-message "none")) (set! foo (lambda (msg) (set! secret-message msg))) (set! bar (lambda () secret-message)))) (display (bar)) ; tulostaa "none" (newline) (foo "meet me by the docks at midnight") (display (bar)) ; tulostaa "meet me by the docks at midnight"
  • Sulkeumia voidaan käyttää oliojärjestelmien toteuttamiseen.

Huomautus: Jotkut puhujat kutsuvat mitä tahansa tietorakennetta, joka sitoo leksikaalisen ympäristön, sulkeumaksi, mutta yleensä termi viittaa erityisesti funktioihin.

Kysymyksiä ja vastauksia

K: Mikä on tietotekniikan päättäminen?


A: Sulkeminen on funktio, jolla on oma ympäristö.

K: Mitä sulkeuman ympäristö sisältää?


V: Sulkeuman ympäristö sisältää vähintään yhden sidotun muuttujan.

K: Kuka antoi closure-idealle sen nimen?


V: Peter J. Landin antoi sulkeutumisen idealle nimen vuonna 1964.

K: Mikä ohjelmointikieli teki sulkeutumisesta suosittua vuoden 1975 jälkeen?


V: Scheme-ohjelmointikieli teki sulkeutumisesta suosittua vuoden 1975 jälkeen.

Kysymys: Ovatko anonyymit funktiot ja sulkeumat sama asia?


V: Anonyymejä funktioita kutsutaan joskus virheellisesti sulkeumiksi, mutta kaikki anonyymit funktiot eivät ole sulkeumia.

K: Mikä tekee nimettömästä funktiosta sulkeuman?


V: Anonyymi funktio on sulkeuma, jos sillä on oma ympäristö, jossa on vähintään yksi sidottu muuttuja.

K: Onko nimetty sulkeuma anonyymi?


V: Ei, nimetty sulkeuma ei ole nimetön.


Etsiä
AlegsaOnline.com - 2020 / 2025 - License CC3