Bildirmeli programlama - Emir programlama


24

Emperative programlama ile kendimi çok rahat hissediyorum. Yapmak istediğim şeyin ne olduğunu çözdüğümde, bilgisayarın ne yapmasını istediğimi algoritmik olarak ifade etmekte hiçbir zaman sorun yaşamam. Fakat SQL gibi dillere gelince ya da sık sık takılıyorum çünkü kafam Emperative programlama için çok kullanılıyor.

Örneğin, ilişki grubu (bandName, bandCountry), mekan (mekanName, venueCountry), oyunlar (bandName, venueName) olduğunu ve şunu söyleyen bir sorgu yazmak istediğinizi varsayalım: tüm mekanNames gibi her grup için bir grup var. o isimde o ülkede oynuyor.

Örnek: Tüm ülkelerden (bandCountry) grupların oynadığı tüm mekanName'lerini istiyorum . Ayrıca, "ilişki" ile bir SQL tablosu demek

Aklımda hemen gidiyorum "her mekanName için tüm bandCountries üzerinde yinelenen her grup için ve her bandCountry ondan gelen grupların listesini alır. Bunlardan hiçbiri mekanName'de oynamıyorsa, bir sonraki mekanName'e gidin. Grupların sonunda iteration, venueName öğesini, iyi venueNames kümesine ekleyin ".

... ama SQL'de böyle konuşamazsınız ve aslında bunu nasıl formüle edeceğimi düşünmeye ihtiyacım var, sezgisel Imperative çözümü sürekli kafamın arkasını sallıyor. Bu problemi başkası yaşadı mı? Bunu nasıl aştın? Bir paradigma değişimi buldun mu? Emperatif çözümleri Declarative olanlara dönüştürmek için Emperatif kavramlardan SQL kavramlarına bir harita yaptınız mı? İyi bir kitap oku?

PS Yukarıdaki sorguya bir çözüm aramıyorum, çözdüm.


1
Bu iyi bir soru, çünkü birçoklarının (kendim dahil) sahip olduğu bir zayıflığı dile getiriyorsunuz.
David Weiser,

Sorunuzda "ilişki" ile ne demek istediğinizi tanımlamak faydalı olabilir. İlişkisel modelde (SQL'in arkasındaki matematik), "ilişki" kabaca bir SQL tablosuna benzer. Pek çok insan gerçekten "ilişki" demek istediklerinde "ilişki" diyecekler.
Jason Baker,

Küme teorisini ve ayrık matematiği öğrenir.

1
@ Jase21, ben şahsen ikisine de aşinayım ama SQL'deki önemsiz şeyler hala komik geliyor. Temiz matematik örneklerinin hiçbiri gerçek dünyadaki garip şeylerle ilgilenmiyor. Ek olarak, LINQ kullanılabilir ve bu nedenle SQL ile rahatsız edilmeyebilirsiniz. Sonunda, asker için: zamanla alışacaksın.
İş

Yanıtlar:


12

İşleri bildirimsel olarak yapmanın arkasındaki fikir , neyi değil, neyi belirtmeniz gerektiğidir .

Bana göre doğru yoldasın. Sorun, şeyleri yanlış yoldan düşünmeniz değildir. Çok ileri gidiyorsun. Yapmaya çalıştığınız şeye bakalım:

Örneğin, ilişki grubu (bandName, bandCountry), mekan (mekanName, venueCountry), oyunlar (bandName, venueName) olduğunu ve şunu söyleyen bir sorgu yazmak istediğinizi varsayalım: tüm mekanNames gibi her grup için bir grup var. o isimde o ülkede oynuyor.

Şimdiye kadar, bu harika. Ama sonra bunu yaparsın:

Aklımda hemen gidiyorum "her mekanName için tüm bandCountries üzerinde yinelenen her grup için ve her bandCountry ondan gelen grupların listesini alır. Bunlardan hiçbiri mekanName'de oynamıyorsa, bir sonraki mekanName'e gidin. Grupların sonunda iteration, venueName öğesini, iyi venueNames kümesine ekleyin ".

Özünde, gereksiz bir iş yapıyorsun. Ne istediğini biliyorsun , ki gerçekten ihtiyacın olan bu. Ama sonra sen devam edersin ve nasıl elde edeceğini bulmaya çalışırsın .

Yerinde olsam şu alışkanlığı kazanmaya çalışırdım:

  1. Ne istediğini tanımla .
  2. Kendinizi nasıl alacağınızı tanımlamaktan bilinçli olarak kendinizi durdurun .
  3. SQL'de istediğinizi nasıl temsil edeceğinizi öğrenin.

Sizin için biraz zaman ve çaba gerektirebilir, ancak bildirimsel programlamayı gerçekten tamamladığınızda, çok faydalı olur. Aslında, kodunuzun geri kalanında bildirimsel programlama kullanarak kendinizi bulabilirsiniz.

Bir kitap arıyorsanız, SQL ve İlişkisel Teoriyi öneririm . Gerçekten de SQL veritabanlarının arkasındaki teoriyi anlamanıza yardımcı olur. Sadece Date'in tavsiyelerini bir tuz tuzu ile almayı unutmayın. Çok iyi bilgi veriyor, ancak zaman zaman biraz tartışılabilir.


Bir şeyi nasıl elde edeceğimi anlamanın yanlış bir yaklaşım olduğunu anlamıyorum. Ne tür bir dil kullandığın önemli değil, ne istediğini yapması için nasıl söyleyeceğini bulmak zorundasın.
davidk01

9

kümeler açısından düşünün, yineleyiciler değil; sql deyimleri istenen çıktı kümesinin özelliklerini tanımlar (aka tablo / ilişki)

tüm mekanNames, öyle ki her grup için, bu ülkeden o isimdeki yerde oynayan bir grup var.

Bunun sonucu (niyetinizi doğru anladıysam!) o mekanda en az bir gruba sahip olan mekânlar olurdu. BandCountry üzerindeki yineleme gereksizdir, çünkü PLAYS ilişkisi zaten aradığınız bilgilere sahip olduğundan, yinelemeleri kaldırmanız gerekir.

yani SQL'de bu olacaktır:

select 
    distinct venueName
from PLAYS

EDIT: tamam, yani istenen gerçek set biraz daha karmaşık. Veritabanından sorulan soru şudur: Hangi ülkelerde tüm ülkelerden gruplar yer almıştır ?

Böylece, istenen kümenin bir öğesi için üyelik ölçütlerini hedef olarak tanımlarız, sonra kümeyi doldurmak için geriye doğru çalışırız. Her ülkeden en az bir grup için bir PLAYS sırası varsa, bir yer setin bir üyesidir. Bu bilgiyi nasıl alırız?

Bunun bir yolu, her bölge için farklı ülkeleri saymak ve onu tüm ülkelerin sayısı ile karşılaştırmaktır. Ancak ÜLKE ilişkimiz yok. Bir an için verilen modeli düşünürsek, tüm ülkelerin kümesinin doğru kriterler olmadığını görüyoruz; en az bir gruba sahip tüm ülkelerin kümesidir. Bu yüzden bir ülke masasına ihtiyacımız yok (normalize edilmiş bir model için sahip olmamız gerekse de) ve mekanın ülkesini umursamıyoruz, sadece gruplara sahip ülkeleri sayabiliriz, örneğin (MS-SQL’de )

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

Her bölge için grup ülkeleri sayabiliriz

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

ve bir alt sorgu kullanarak ikisini bir araya getirebiliriz

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

Şimdi, mümkün olan en güzel sorgu bu değil (GROUP BY ve HAVING, geçici değişkenlerden ve bir alt sorgudan daha 'şık' bir çözüm olarak görülebilir) ancak peşinde olduğumuz şey oldukça açıktır, bu nedenle OP'nin amacına bırakacağız. .

OP'nin amacı, zihniyetin zorunlu olandan bildirimselliğe nasıl geçirileceğini öğrenmek oldu. Bu amaçla, açıklanan zorunlu çözümün ne yaptığına bakın:

her mekanName için tüm bandCountries üzerinde yineleme yapın ve her bandCountry için ondan gelen grupların listesini alın. Hiçbiri mekanName'de oynamıyorsa, bir sonraki mekana geç. Aksi halde, bandın sonundaCountries yineleme, iyi bir mekan olan Names'e venueName ekleyin.

Yukarıdakilerde belirleyici kriterler nedir? Sanırım öyle:

... hiçbiri [belirli bir ülkeden grupların grubu] mekanName'de oynamazsa ...

Bu diskalifiye edici bir kriterdir . Zorunlu düşünce süreci tam bir kova ile başlıyor ve kriterlere uymayan şeyler atıyor. Biz ediyoruz filtreleyerek verileri.

Bu basit şeyler için iyidir, ancak istenen sonuç kümesinin oluşturulması açısından düşünmeye yardımcı olur; İlgili nedir eleme kriterleri biri yerine kovayı doldurmak için izin verecek?

  • diskalifiye edici: Eğer bir mekanda oynayan bir bandCountry'den grup yoksa, mekan diskalifiye edilir.
  • (kısmi) niteleyici: bir bandCountry'den en az bir grup bir mekanda oynarsa, mekan iyi olabilir; bandın geri kalanını kontrol etmeye devam et
  • (tam) niteleyici: Her bir gruptan en az bir grupCountry bir mekanda oynuyorsa, o yer niteliklidir

Son niteleyici sayıları kullanarak basitleştirilebilir: oradan en az bir grup bir mekanda oynarsa, bir bandCountry 'memnun'; Bir mekan için 'memnun' grup ülke sayısı, mekanın kalifiye olması için grup ülke sayısına eşit olmalıdır.

Artık navigasyon yoluyla ilişkiler arasında sebep olabiliriz:

  • VENUE ilişkisiyle başlayın [cevap için buna ihtiyacımız yok, ancak ilişkisel gezinme için kavramsal başlangıç ​​noktası]
  • venueName PLAYS'e katıl
  • bandCountry'i almak için bandName'deki BAND'a katılın
  • grubun adını umursamıyoruz; sadece mekan seçinAdı ve bandCountry
  • Gereksiz bant sayıları umrumda değil; DISTRICT veya GROUP BY kullanarak kopyaları ortadan kaldırmak
  • isimleri değil, yalnızca farklı bandCountries sayısını önemsiyoruz
  • biz sadece farklı bandCountries sayısının toplam bandCountries sayısı ile aynı olduğu yerlerde istiyoruz.

bu da yukarıdaki çözüme geri götürür (veya makul bir faksal)

ÖZET

  • küme teorisi
  • ilişkisel gezinme yolları
  • kapsayıcı ve özel kriterler dahil

Aslında "onlardan tüm ülkelerden grupların yer aldığı (bandCountry> = venueCountry) mekanlardır".
EpsilonVector

@EpsilonVector: düzenlemelere bakın
Steven A. Lowe

4

Deklaratif bir tarzda düşünmeyi ve programlamayı öğrenmenin bir yolu, APL veya J gibi genel amaçlı bir dizi dili öğrenmektir. SQL, muhtemelen bildirimsel olarak programlamayı öğrenmek için en iyi araç değildir. APL veya J'de açık döngü veya yineleme olmadan tüm dizilerde (vektörler, matrisler veya daha yüksek dereceli diziler) çalışmayı öğrenirsiniz. Bu, SQL ve ilişkisel cebiri anlamayı çok daha kolaylaştırır. Çok basit bir örnek olarak, APL'de değeri 100'den büyük olan bir V vektöründen öğeleri seçmek için APL'de yazıyoruz:

(V>100)/V

Burada V> 100, V ile aynı şekilde olan bir boolean dizisini değerlendirir, 1'de tutmak istediğimiz değerleri işaretler. Tecrübeli APL'de gerçekleşmeyen yinelemelerin oluşmadığı, sadece V vektörüne bir maske uygulayıp, yeni bir vektör döndürüyoruz. Bu, elbette kavramsal olarak, yan tümce veya ilişkisel cebir işlemlerini kısıtlayan bir SQL'in yaptığıdır.

Çok fazla bir şey yapmadan bildirimsel programlama konusunda iyi bir fikir sahibi olabileceğinizi sanmıyorum ve SQL genellikle çok özeldir. Döngü olmadan nasıl yapılacağını ve eğer / sonra / başkaca yapılar ve zorunlu, prosedürel ve skalar tarzı programlamaya katılan tüm aygıtları öğrenmek için birçok genel amaçlı kod yazmanız gerekir.

Bu şekilde düşünmeye yardımcı olan başka fonksiyonel diller de olabilir, ancak dizi dilleri SQL'e çok yakındır.


+1 "[yapamazsınız] iyi bir kavrama ... çok fazla yapmadan". Kimse de bir a = a + 1gecelik zorunlu programlama (bunun gibi açıkça sezgisel yapılarıyla ) öğrenmedi . Zorunlu programlama öğrenmek için zaman harcadığı gibi mantık, fonksiyonel ve benzeri gibi bildirim stillerini öğrenmek zaman alır.
SADECE DOĞRUDAN AĞIMIM

1

İlk önce ikisini de öğrenmek zorundasın. Bir tercihiniz olabilir, ancak diğerinin daha iyi olduğu alanlarda çalışırken, onunla savaşmayın. Birçok programcı imleçleri ilişkisel veritabanlarında kullanmaya özen gösterir, çünkü her kayıtta adım adım ilerler, ancak veritabanı kümelerde çok daha iyidir. "Bu şekilde nasıl yapacağımı biliyorum ve en fazla kontrolüm var, filan, filan, filan" zihniyetine girmek istemezsin.


1

Zorunlu olarak düşünmeyi öğrendiğin gibi bildirimsel olarak düşünmeyi öğrenirsin: basit problemlerle başlayıp pratik yaparak "alırken" üzerinde çalışarak.

Zorunlu programlama konusundaki ilk deneyimleriniz, " a = a + 1" gibi bir dizi karşı-sezgisel (ve aslında tamamen saçma) ifadeleri içeriyordu . Zihnini bunun etrafına sardın, şimdi de muhtemelen ifadenin bariz gerçeğinden geri tepmeyi bile hatırlamadın. Beyanname stilleriyle ilgili probleminiz, zorunlu stillerle ilk başladığınızda, bulunduğunuz yere geri dönmenizdir: "ipucu olmayan bir yeniler". Daha da kötüsü, bu yeni stille tamamen çelişen bir stille yıllarca pratik yaptınız ve “her ne pahasına olursa olsun kontrol etme” alışkanlığı gibi geri alma alışkanlığınız var.

Beyanname stilleri, şu an için sezgiden yoksun olduğunuz farklı bir yaklaşımla çalışır (matematik becerilerinizi yıllar boyunca çok keskin tutmazsanız - ki bu çoğu insanın yapmadığı). Nasıl düşüneceğinizi yeniden öğrenmelisiniz ve bunu tekrar öğrenmenin tek yolu bunu yapmaktır, her seferinde basit bir adım.

Kavramları gerçekten öğrenmek istiyorsanız, SQL'i bildirimsel programlamaya ilk adımınız olarak seçmek yanlış olabilir. Tabii dayandığı matematik hesabının, alabildiğince beyan edici olduğundan emin olun, ancak maalesef, matematik hesabının saflığı, uygulama gerçekleri tarafından kötü bir şekilde tehlikeye sokuldu ve dil, aslında, karışık bir karmaşa haline geldi. Sen gibi (anlamda alışık) diğer daha doğrudan kullanışlı de bildirim dilleri bakmak yerine isteyebilirsiniz Lisps (özellikle. Şema ), Haskell ve ML'leri (çoğunlukla) fonksiyonel programlama için veya alternatif olarak, Prolog ve Merkür için (çoğunlukla) mantık programlama.

Bu diğer dilleri öğrenmek, bence, bildirimsel programlamanın birkaç nedenden dolayı nasıl çalıştığı hakkında size daha iyi bir fikir verecektir:

  1. Bunlar "beşikten mezara" programlama için kullanışlıdır - bu dillerde baştan sona tam bir program yazabileceğiniz gibi. Tek başına ayakta durmak faydalıdır, çoğu insan için bağımsız bir dil olarak oldukça yararsız olan SQL'den farklı olarak.

  2. Her biri size nihayet "almak" için farklı yollar sağlayabilen bildirimsel programlama konusunda farklı bir eğilime sahiptir.

  3. Ayrıca, her biri genel olarak programlama hakkında düşünme konusunda size farklı bir eğiklik sunuyor. Hiçbir zaman doğrudan kendiniz kullanmasanız bile, problemler hakkında düşünme ve kodlama yeteneğinizi geliştirir.

  4. Onlardan öğrendiğiniz dersler, SQL'inizde de size yardımcı olacaktır - özellikle veriler hakkında saf düşünme biçimi için ilişkisel veritabanlarının ardındaki hesabı hesaplarsanız.

Özellikle işlevsel dillerden birini (Lisps'ten biri olan Clojure , muhtemelen burada iyi bir seçimdir) ve mantık dillerinden birini (Mercury'yi en çok seviyorum ama Prolog'un öğrenme için çok daha yararlı bir materyali var) öğrenmesini tavsiye ederim. azami düşünce süreci genişlemesi için.


1

SQL gibi bildirimsel bir ortamda zorunlu olarak düşünmek yanlış değildir. Sadece zorunlu düşüncenin tanımladığınızdan biraz daha yüksek bir seviyede olması gerekir. Ne zaman SQL kullanan bir veritabanını sorgulamaya ihtiyacım olursa, daima kendime düşünüyorum:

  • İşte ihtiyacım olan parçalar.
  • Onları bu şekilde bir araya getireceğim.
  • Asıl aradığımı elde etmek için, aşağıdaki öngörülerde elde ettiğim şeyi küçümseyeceğim.

Yukarıdakiler, üst düzey bir zorunlu algoritmadır ve SQL ayarında benim için oldukça iyi çalışıyor. Bunun yukarıdan aşağıya bir yaklaşım olarak kabul edildiğini düşünüyorum ve Steven A. Lowe oldukça iyi bir aşağıdan yukarıya yaklaşımı açıklıyor .


1

Sorunuzun anahtarı, sondan sonraki paragrafta söylediklerinizde: "SQL'de böyle konuşamazsınız." Bu aşamada, SQL'e programlama dili yerine yabancı bir dil olarak yaklaşmanız sizin için daha yararlı olabilir. Bu şekilde düşünürseniz, bir SQL sorgusu yazmak gerçekten ne istediğinizi İngilizce bir deyim "SQLish" e çevirmektir. Bilgisayar, SQLish'i mükemmel bir şekilde anlıyor ve söylediklerinizi tam olarak yapacak; bu nedenle doğru çevirdiğiniz sürece uygulama konusunda endişelenmenize gerek yok.

Bununla birlikte, bir yabancı dili öğrenmenin en iyi yolu nedir? Belli ki, SQL belgelerinizden alabileceğiniz dilbilgisi ve kelimeleri öğrenmeniz gerekir. En önemli şey pratiktir. Yazabildiğiniz kadar SQL okuyup yazmalısınız ve ilk önce sözdizimini iyi bilmeniz gerektiğini düşünmeyin; ilerlerken bazı şeyleri araştırabilir ve aramalısınız. SQL'de hangi verileri açıklamak istediğinizi İngilizce'den daha kolay tanımladığınızda daha kolay olduğunu bileceksiniz.


1

Kafamı SQL'in etrafına da sarmak çok zaman aldı. Üniversitede ve o zamanlar, sadece meseleleri karmaşıklaştırmaya yarayan ilişkisel teori yaptık. Sonunda, öğrenme sürecim çok çeşitli deneme materyalleri ve yol boyunca faydalı bulduğum örnekler tarafından bilgilendirilen deneme yanılma oldu. Temel olarak, sonunda buna alışacaksınız ve veri ve sorgular hakkında yeni bir düşünme yöntemi eklemek zihinsel gelişiminiz için bir miktar değer yaratacaktır.

Her dil özelliğinin nasıl kullanılacağını ve bilinen bir masa üzerinde belirli sonuçların nasıl elde edileceğini (referans için verilen tablo tanımları) gösteren basit bir komut dosyası koleksiyonu oluşturarak öğrenmemi hızlandıracağımı öğrendim.

Bu yılın başlarında, dağınık bir Oracle veritabanında bir veri taşıma projesini içeren bazı resmi eğitimler yaptım; burada, sorgu sonuçlarını tam olarak istediklerime kadar çeşitli şekillerde filtrelemek için kütüphanemden parçaları yavaşça bir araya getirdim, sonra bunları gerekli ve benzeri. Sorguların bazıları çok karmaşık ve hata ayıklamak zorlaştı. Şimdi onları okuyabileceğimden şüpheliyim, ancak referans yapı taşlarımı kullanarak benzer bir çözüme ulaşabileceğimi umuyorum.

Bilgilendirici ve işlevsel alanlar hakkındaki sezgisel farkındalığınızı arttırmanın diğer yolları, belirli bir paradigmaya daha uygun öğrenme kümesi teorisi ve programlama dilleridir. Şu anda bazı Haskell öğrenme sürecindeyim, örneğin matematiksel yeteneklerimi sürdürmek ve geliştirmek.


0

Bir problemle karşılaştığınızda genellikle nasıl çözeceğinizi düşünürsünüz. Ama bilgisayarın sizin için nasıl çözdüğünü biliyorsanız! O zaman nasıl ortadan kaldırılacağına dair endişelisin .

Nasıl olduğunu söylemeye çalışıyorum.

Özyinelemeli programlara zaten aşina olabilirsiniz, özyinelemeli programlarda sorunu nasıl çözüleceğini söylemek yerine tanımlarsınız . Eğer tanımlayan taban ve tanımlamak n göre n-1 . (örneğin factorial(n) = n * factorial(n-1)) Ancak , bilgisayarın nasıl çözdüğünü zaten biliyor olabilirsiniz . işlevden başlar ve temel tanımlamaya ulaşana kadar işlevi tekrar tekrar çağırır, ardından diğer tüm işlevleri temel değere göre değerlendirir.

Bu bildirimsel programlamada olan şey. Her şeyi mevcut tanımlara göre tanımlarsınız. Bilgisayar temel cevabı temel alarak sizin için cevabı nasıl türeteceğinizi bilir.

SQL'de tanımları birbiriyle ilişkilendirmeyebilirsiniz, ancak nesneleri veya bilgileri birbiriyle ilişkilendirirsiniz, ne istediğinizi belirtir ve bilgisayar aramayı, verdiğiniz ilişkilere dayanarak bir şeyle (nesne, bilgiler) belirlersiniz.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.