Programowanie deklaratywne a programowanie imperatywne

24

Czuję się bardzo dobrze z programowaniem imperatywnym. Nigdy nie mam problemu z wyrażeniem algorytmicznym tego, co chcę, aby komputer zrobił, gdybym zorientował się, co mam zrobić. Ale jeśli chodzi o języki takie jak SQL lub często utknąłem, ponieważ moja głowa jest zbyt przyzwyczajona do programowania imperatywnego.

Załóżmy na przykład, że masz zespół relacji (bandName, bandCountry), venue (venueName, venueCountry), odtworzenia (bandName, venueName), a ja chcę napisać zapytanie, które brzmi: wszystkie nazwy venue takie, że dla każdego bandCountry jest zespół z kraj, który gra w miejscu o tej nazwie.

Przykład: Chcę, aby wszystkie nazwy miejsc, w których grały zespoły ze wszystkich krajów (bandCountry). Również przez „relację” rozumiem tabelę SQL.

W mojej głowie od razu wybieram „dla każdego miejsca nazwa-miejsca iteruje po wszystkich zespołach-krajach i dla każdego zespołu-państw otrzymuję listę zespołów, które z niego pochodzą. Jeśli żadne z nich nie gra w miejscu-nazwa, przejdź do następnego miejsca-nazwa. W przeciwnym razie na końcu zespołu iteracja dodaj venueName do zestawu dobrych venueName ".

... ale nie można tak mówić w języku SQL i muszę pomyśleć o tym, jak to sformułować, a intuicyjne rozwiązanie Imperative ciągle dręczy mnie z tyłu głowy. Czy ktoś miał ten problem? Jak sobie z tym poradziłeś? Czy wymyśliłeś zmianę paradygmatu? Czy stworzyłeś mapę od koncepcji imperatywnych do koncepcji SQL, aby przetłumaczyć rozwiązania imperatywne na deklaratywne? Przeczytaj dobrą książkę?

PS Nie szukam rozwiązania powyższego zapytania, rozwiązałem je.

EpsilonVector
źródło
1
To dobre pytanie, ponieważ wyrażasz słabość, którą ma wiele (w tym ja).
David Weiser
Przydatne może być zdefiniowanie, co rozumiesz przez „relację” w swoim pytaniu. W modelu relacyjnym (matematyka za SQL) „relacja” jest z grubsza analogiczna do tabeli SQL. Wiele osób powie „związek”, gdy naprawdę chce powiedzieć „związek”.
Jason Baker
Naucz się teorii mnogości i dyskretnej matematyki.
1
@ Jase21, osobiście znam oba, ale nietrywialne rzeczy w SQL nadal są zabawne. Żaden z czystych przykładów matematycznych nie dotyczy dziwnych rzeczy w świecie rzeczywistym. Dodatkowo można użyć LINQ, a tym samym nie przejmować się SQL. Wreszcie do pytającego: przyzwyczaisz się do tego z czasem.
Job

Odpowiedzi:

12

Pomysł robienia rzeczy deklaratywnie polega na tym, że należy określić, co , a nie jak .

Dla mnie brzmi, jakbyś był na dobrej drodze. Problemem nie jest to, że myślisz o rzeczach w niewłaściwy sposób. Chodzi o to, że posuwasz się za daleko. Spójrzmy na to, co próbujesz zrobić:

Załóżmy na przykład, że masz zespół relacji (bandName, bandCountry), venue (venueName, venueCountry), odtworzenia (bandName, venueName), a ja chcę napisać zapytanie, które brzmi: wszystkie nazwy venue takie, że dla każdego bandCountry jest zespół z kraj, który gra w miejscu o tej nazwie.

Jak dotąd jest to świetne. Ale potem robisz to:

W mojej głowie od razu wybieram „dla każdego miejsca nazwa-miejsca iteruje po wszystkich zespołach-krajach i dla każdego zespołu-państw otrzymuję listę zespołów, które z niego pochodzą. Jeśli żadne z nich nie gra w miejscu-nazwa, przejdź do następnego miejsca-nazwa. W przeciwnym razie na końcu zespołu iteracja dodaj venueName do zestawu dobrych venueName ".

Zasadniczo wykonujesz niepotrzebną pracę. Wiesz, czego chcesz, a to wszystko, czego naprawdę potrzebujesz. Ale potem idź dalej i spróbuj dowiedzieć się, jak go zdobyć.

Gdybym był tobą, starałbym się nabrać następującego nawyku:

  1. Zdefiniuj, co chcesz.
  2. Świadomie powstrzymaj się od określenia, jak go zdobyć.
  3. Dowiedz się, jak reprezentować to, co chcesz w SQL.

Może to zająć trochę czasu i wysiłku z twojej strony, ale kiedy naprawdę zaczniesz programować deklaratywnie, staje się to bardzo przydatne. W rzeczywistości możesz znaleźć deklaratywne programowanie w pozostałej części kodu.

Jeśli szukasz książki, polecam SQL i teorię relacyjną . To naprawdę pomaga zrozumieć teorię baz danych SQL. Pamiętaj tylko, aby wziąć zalecenia Date z odrobiną soli. Udziela bardzo dobrych informacji, ale czasem może być nieco uparty.

Jason Baker
źródło
Nie rozumiem, jak wymyślenie, jak coś zdobyć, jest niewłaściwym podejściem. Nie ma znaczenia, jakiego języka używasz, musisz dowiedzieć się, jak powiedzieć mu, aby zrobił to, co chcesz.
davidk01
9

myśl w kategoriach zbiorów, a nie iteratorów; instrukcje sql definiują właściwości pożądanego zestawu danych wyjściowych (inaczej tabela / relacja)

all venue Nazwy takie, że dla każdego zespołu Country istnieje zespół z tego kraju, który gra w miejscu o tej nazwie

wynikiem tego (jeśli dobrze zrozumiałem twoje intencje!) byłby zestaw miejsc, w których przynajmniej jeden zespół gra w tym miejscu. Iteracja nad bandCountry nie jest konieczna, ponieważ relacja PLAYS już zawiera informacje, których szukasz, po prostu musisz wyeliminować duplikaty

więc w SQL byłoby to:

select 
    distinct venueName
from PLAYS

EDYCJA: ok, więc faktyczny pożądany zestaw jest nieco bardziej skomplikowany. Baza danych zadaje pytanie: jakie miejsca gościły zespoły ze wszystkich krajów?

Tak więc definiujemy kryteria członkostwa dla elementu pożądanego zestawu jako cel, a następnie pracujemy wstecz, aby wypełnić zestaw. Miejsce jest członkiem zestawu wyników, jeśli ma wiersz PLAYS dla co najmniej jednego zespołu z każdego kraju. Jak uzyskać te informacje?

Jednym ze sposobów jest policzenie różnych krajów dla każdego miejsca i porównanie ich z liczbą wszystkich krajów. Ale nie mamy relacji KRAJ. Jeśli zastanowimy się przez chwilę nad modelem, zobaczymy, że zbiór wszystkich krajów nie jest właściwym kryterium; to zbiór wszystkich krajów, które mają co najmniej jeden zespół. Nie potrzebujemy więc tabeli kraju (chociaż w przypadku znormalizowanego modelu powinniśmy ją mieć) i nie dbamy o kraj miejsca, możemy po prostu policzyć kraje, które mają pasma, np. (W MS-SQL )

declare @BandCountryCount int
select
    @BandCountryCount = COUNT(distinct bandCountry)
from BAND

Możemy policzyć kraje zespołu dla każdego miejsca

select
    P.venueName, COUNT(distinct B.bandCountry) as VenueBandCountryCount
from PLAYS P
    inner join BAND B on B.bandName = P.bandName

i możemy poskładać je razem za pomocą podzapytania

select
    venueName
from (
    select
        P.venueName, COUNT(distinct B.bandCountry) as VenueBandCountryCount
    from PLAYS P
        inner join BAND B on B.bandName = P.bandName
) X
where X.VenueBandCountryCount = @BandCountryCount

To nie jest najładniejsze możliwe zapytanie (GROUP BY i HAVING można uznać za bardziej „eleganckie” rozwiązanie niż zmienne tymczasowe i podzapytanie), ale to dość oczywiste, o co nam chodzi, więc zostawimy to na cel PO .

Celem PO było nauczenie się, jak zmienić sposób myślenia z imperatywnego na deklaratywny. W tym celu spójrz na to, co robiło opisane imperatywne rozwiązanie:

dla każdego miejsca Nazwa powtarza się po wszystkich bandCountries i dla każdego bandCountry pobierz listę zespołów, które z niego pochodzą. Jeśli żadne z nich nie gra w venueName, przejdź do następnego venueName. W przeciwnym razie na końcu pasma iteracji Krajów dodaj venueName do zestawu dobrych venueNames

Jakie są powyższe kryteria? Myślę, że to jest:

... Jeśli żaden z nich [zestaw zespołów z określonego kraju] nie gra w VenueName ...

To są kryteria dyskwalifikujące . Konieczny proces myślenia rozpoczyna się od pełnego wiadra i wyrzucania rzeczy, które nie spełniają kryteriów. Jesteśmy filtrowanie danych.

To proste w przypadku prostych rzeczy, ale pomaga myśleć w kategoriach konstruowania pożądanego zestawu wyników; jakie są odpowiednie kryteria kwalifikacyjne , które pozwoliłyby zamiast tego wypełnić wiadro?

  • dyskwalifikator: jeśli nie ma zespołu z kraju, który gra w danym miejscu, miejsce jest zdyskwalifikowane
  • (częściowy) kwalifikator: jeśli co najmniej jeden zespół z bandCountry gra w danym miejscu, to miejsce może być w porządku; sprawdzaj resztę bandCountries
  • (pełny) kwalifikator: jeśli co najmniej jeden zespół z każdego zespołu Kraj gra w miejscu, to miejsce jest kwalifikowane

Ostateczny kwalifikator można uprościć za pomocą liczników: bandCountry jest „zadowolony”, jeśli przynajmniej jeden zespół stamtąd gra w danym miejscu; liczba „zadowolonych” krajów zespołu dla danego miejsca musi być równa liczbie krajów zespołu dla danego miejsca, które ma zostać zakwalifikowane.

Teraz możemy uzasadnić relacje między nawigacjami:

  • zacznij od relacji MIEJSCE [nie potrzebujemy jej do odpowiedzi, ale jest to koncepcyjny punkt wyjścia do nawigacji relacyjnej]
  • dołącz do PLAYS na venueName
  • dołącz do BAND na bandName, aby uzyskać bandCountry
  • nie obchodzi nas nazwa zespołu; wybierz tylko nazwę miejsca i bandCountry
  • nie dbamy o redundantne bandCountries; eliminuj duplikaty za pomocą DISTRICT lub GROUP BY
  • zależy nam tylko na liczbie odrębnych krajów band, a nie na nazwach
  • chcemy tylko miejsc, w których liczba odrębnych bandCountries jest taka sama jak całkowita liczba bandCountries

co prowadzi do powyższego rozwiązania (lub jego uzasadnionego faksu)

PODSUMOWANIE

  • teoria mnogości
  • relacyjne ścieżki nawigacji
  • kryteria włączające vs wyłączne (kwalifikacje vs dyskwalifikacja)
Steven A. Lowe
źródło
To właściwie „zestaw miejsc, w których grali w nich zespoły ze wszystkich krajów (bandCountry> = venueCountry)”.
EpsilonVector,
@EpsilonVector: patrz zmiany
Steven A. Lowe,
4

Jednym ze sposobów nauczenia się myślenia i programowania w stylu deklaratywnym jest nauczenie się języka tablic ogólnego przeznaczenia, takiego jak APL lub J. SQL prawdopodobnie nie jest najlepszym narzędziem do nauki deklaratywnego programowania. W APL lub J uczysz się obsługiwać całe tablice (wektory, macierze lub tablice wyższej rangi), bez wyraźnego zapętlania lub iteracji. To znacznie ułatwia zrozumienie algebry SQL i relacyjnej. Jako bardzo prosty przykład, aby wybrać elementy z wektora V, których wartość jest większa niż 100, w APL piszemy:

(V>100)/V

Tutaj V> 100 przyjmuje wartość logiczną do tablicy o tym samym kształcie co V, gdzie 1 oznacza wartości, które chcemy zachować. Doświadczonemu APLerowi nie przychodzi do głowy, że trwa iteracja, po prostu nakładamy maskę na wektor V, zwracając nowy wektor. Jest to oczywiście koncepcyjnie to, co robi operacja SQL, w której klauzula lub relacyjna algebra ograniczają działanie.

Nie sądzę, że można dobrze opanować programowanie deklaratywne bez robienia tego dużo, a SQL ogólnie jest zbyt specyficzny. Musisz napisać dużo kodu ogólnego przeznaczenia, nauczyć się, jak obchodzić się bez pętli i struktur if / then / else, a także całego aparatu, który uczestniczy w programowaniu imperatywnym, proceduralnym i skalarnym.

Mogą istnieć również inne języki funkcjonalne, które pomagają w takim sposobie myślenia, ale języki tablicowe są bardzo zbliżone do SQL.

PAUL Mansour
źródło
+1 za „[nie możesz] uzyskać dobrego uścisku ... bez robienia tego dużo”. Nikt też nie nauczył się programowania imperatywnego (z jego wyraźnie sprzecznymi z intuicją konstrukcjami podobnymi a = a + 1) przez noc. Nauczenie się stylów deklaratywnych, takich jak logika, funkcjonalność, zajmuje trochę czasu, podobnie jak nauczenie się programowania imperatywnego.
PO PROSTU MOJA poprawna OPINIA
1

Najpierw musisz nauczyć się obu. Możesz mieć preferencje, ale pracując w obszarach, w których drugi jest lepszy, nie walcz z nim. Wielu programistów ma pokusę używania kursorów w relacyjnych bazach danych, ponieważ są przyzwyczajeni do przechodzenia przez każdy rekord, ale baza danych jest znacznie lepsza w zestawach. Nie chcesz wejść w sposób myślenia: „Wiem, jak to zrobić w ten sposób i mam największą kontrolę, bla, bla, bla”.

JeffO
źródło
1

Nauczysz się myśleć deklaratywnie, tak jak nauczyłeś się myśleć imperatywnie: ćwicząc zaczynając od prostszych problemów i pracując nad tym, jak „rozumiesz”.

Twoje pierwsze doświadczenia z programowaniem imperatywnym obejmowały całą masę sprzecznych z intuicją (i wręcz absurdalnie) stwierdzeń takich jak „ a = a + 1”. Otoczyłeś to umysłem do tego stopnia, że ​​teraz prawdopodobnie nie pamiętasz nawet odrzutu z oczywistej nieprawdy tego stwierdzenia. Problem ze stylami deklaratywnymi polega na tym, że powróciłeś tam, gdzie byłeś, kiedy zacząłeś ze stylami imperatywnymi: „nieświadomym nowością”. Co gorsza, masz lata praktyki z jednym stylem, który jest całkowicie sprzeczny z tym nowym stylem i masz lata nawyków, aby je cofać - na przykład nawyk „kontrolowania za wszelką cenę”.

Style deklaratywne działają z innym podejściem, na razie brakuje ci intuicji (chyba że z biegiem lat utrzymywałeś bardzo matematyczne umiejętności - czego większość ludzi nie ma). Musisz nauczyć się, jak myśleć i jedynym sposobem, aby nauczyć się tego robić, jeden prosty krok na raz.

Wybór SQL jako pierwszego wyjścia do deklaratywnego programowania może być błędem, jeśli naprawdę chcesz nauczyć się pojęć. Jasne, że rachunek krotek, na którym jest oparty, jest tak naprawdę deklaratywny, jak to tylko możliwe, ale niestety czystość rachunku krotek została poważnie naruszona przez realia implementacji, a język stał się trochę mętnym bałaganem. Zamiast tego możesz spojrzeć na inne bardziej użyteczne (w tym sensie, do jakiego jesteś przyzwyczajony) języki deklaratywne, takie jak Lisps (zwłaszcza Scheme ), Haskell i ML dla (głównie) programowania funkcjonalnego lub, alternatywnie, Prolog i Mercury dla (głównie) programowanie logiki.

Nauka tych innych języków zapewni moim zdaniem lepszy wgląd w działanie deklaratywnego programowania z kilku powodów:

  1. Przydają się do programowania „od kołyski do grobu” - ponieważ możesz napisać pełny program w tych językach od początku do końca. Są przydatne samodzielnie, w przeciwieństwie do SQL, który jest naprawdę bezużyteczny dla większości ludzi jako samodzielny język.

  2. Każdy z nich ma inne podejście do deklaratywnego programowania, które może dać ci różne drogi, by w końcu „zdobyć”.

  3. Każdy z nich daje inne podejście do programowania w ogóle. Poprawią twoją zdolność rozumowania problemów i kodowania, nawet jeśli sam nigdy ich nie użyjesz.

  4. Lekcje, których się z nich nauczysz, pomogą ci również w posługiwaniu się językiem SQL - szczególnie jeśli odświeżysz rachunek krotek za relacyjnymi bazami danych, aby uzyskać czystą formę myślenia o danych.

Szczególnie polecam naukę jednego z języków funkcjonalnych ( Clojure , jako jeden z Lisps, jest prawdopodobnie dobrym wyborem tutaj) i jednego z języków logicznych (lubię Mercury'ego najbardziej, ale Prolog ma o wiele bardziej użyteczny materiał do nauki) dla maksymalnego rozszerzenia procesu myślowego.

WŁAŚNIE MOJA poprawna OPINIA
źródło
1

Nie jest źle myśleć imperatywnie w deklaratywnych ustawieniach, takich jak SQL. Po prostu imperatywne myślenie powinno odbywać się na poziomie nieco wyższym niż to, co opisałeś. Ilekroć muszę zapytać bazę danych, która korzysta z SQL, zawsze myślę sobie:

  • Oto elementy, których potrzebuję.
  • Złożę je w ten sposób.
  • Zamierzam skrócić to, co właśnie uzyskałem, z następujących predykatów, aby dostać się do tego, czego naprawdę szukam.

Powyżej jest algorytm imperatywny wysokiego poziomu i działa całkiem dobrze dla mnie w ustawieniach SQL. Myślę, że jest to uważane za podejście top-down i Steven A. Lowe opisać całkiem dobry oddolne podejście.

davidk01
źródło
1

Kluczem do twojego pytania jest to, co powiedziałeś w ostatnim akapicie: „Nie możesz tak mówić w języku SQL”. Na tym etapie może być bardziej użyteczne podejście do SQL jako języka obcego zamiast języka programowania. Jeśli pomyślisz o tym w ten sposób, napisanie zapytania SQL naprawdę tłumaczy angielską instrukcję na „SQLish”. Komputer doskonale rozumie SQLish i zrobi dokładnie to, co mówisz, więc nie musisz się martwić o implementację, o ile poprawnie tłumaczysz.

To powiedziawszy, jaki jest najlepszy sposób na naukę języka obcego? Oczywiście musisz nauczyć się gramatyki i słownictwa, które możesz uzyskać z dokumentacji SQL. Najważniejsza jest praktyka. Powinieneś czytać i pisać jak najwięcej SQL, i nie czujesz, że najpierw musisz dokładnie znać składnię; możesz i powinieneś szukać rzeczy w miarę postępów. Będziesz wiedział, że go masz, gdy łatwiej jest opisać, jakie dane chcesz w SQL niż w języku angielskim.

Larry Coleman
źródło
1

Zajęło mi również dużo czasu, aby owinąć głowę wokół SQL. Na uniwersytecie zrobiliśmy teorię relacyjną, która tylko skomplikowała sprawę. Ostatecznie mój proces uczenia się opierał się na wielu próbach i błędach opartych na różnych materiałach do nauki i przykładach, które uznałem za przydatne po drodze. Zasadniczo w końcu się do tego przyzwyczaisz, a dodanie nowego sposobu myślenia o danych i zapytaniach będzie miało pewną wartość dla twojego rozwoju umysłowego.

Odkryłem, że byłem w stanie przyspieszyć naukę, stopniowo budując kolekcję prostych skryptów pokazujących, jak korzystać z każdej funkcji języka i jak osiągnąć określone wyniki na znanej tabeli (definicje tabel podano w celach informacyjnych).

Na początku tego roku odbyłem formalne szkolenie obejmujące projekt migracji danych w niechlujnej bazie danych Oracle, w której musiałem stopniowo składać fragmenty z mojej biblioteki, aby filtrować wyniki zapytań na różne sposoby, aż do uzyskania dokładnie tego, czego chciałem, a następnie przekształcić je w konieczne i tak dalej. Niektóre zapytania stały się bardzo złożone i trudne do debugowania. Wątpię, czy mógłbym je teraz przeczytać, ale mam nadzieję, że uda mi się ponownie znaleźć podobne rozwiązanie, korzystając z referencyjnych elementów konstrukcyjnych.

Innymi sposobami na zwiększenie intuicyjnej świadomości przestrzeni deklaratywnych i funkcjonalnych są nauka teorii mnogości i języki programowania bardziej dostosowane do określonego paradygmatu. Obecnie uczę się Haskella, na przykład, aby utrzymać i poprawić swoje zdolności matematyczne.

IanGilham
źródło
0

Kiedy napotykasz problem, zwykle myślisz, jak go rozwiązać. Ale jeśli wiesz, jak komputer rozwiązuje to za Ciebie! W takim razie martwisz się, jak zostaniesz wyeliminowany.

Próbuję powiedzieć, jak to się dzieje.

Być może znasz już programy rekurencyjne, w programach rekurencyjnych definiujesz problem, a raczej określasz sposób jego rozwiązania. Ci określić bazę, a następnie określ n na podstawie n-1 . (na przykład factorial(n) = n * factorial(n-1)) Ale możesz już wiedzieć, jak komputer to rozwiązuje. zaczyna się od funkcji i wywołuje funkcję rekurencyjnie, aż osiągnie podstawową definicję, a następnie ocenia wszystkie pozostałe funkcje na podstawie wartości podstawowej.

Tak dzieje się w programowaniu deklaratywnym. definiujesz wszystko na podstawie istniejących definicji. A komputer wie, jak uzyskać odpowiedź na podstawie podstawowych funkcji.

W SQL nie możesz powiązać definicji między sobą, ale odnosisz obiekty lub informacje, określasz to, czego chcesz, i wyszukujesz komputer na podstawie czegoś (obiektu, informacji) na podstawie podanych przez ciebie relacji.

Ahmad
źródło