C aslında Turing tamamlandı mı?


39

C'ye Turing'in eksiksiz olduğunu açıklamaya çalışıyordum ve gerçekten de teknik olarak Turing'in eksiksiz olup olmadığını bilmediğimi fark ettim. (Soyut anlambilimdeki gibi C, gerçek bir uygulamadaki gibi değil.)

"Açıkça" cevabı (kabaca: keyfi bir hafıza miktarına hitap edebilir, bu yüzden bir RAM makinesini taklit edebilir, bu yüzden Turing-tamamlandı), C standardının izin verdiği ölçüde söyleyebildiğim kadarıyla aslında doğru değil size_t'nin keyfi olarak büyük olması için, belirli bir uzunlukta sabitlenmesi gerekir ve hangi uzunlukta olursa olsun sabitlenmesi yine de sonludur. (Başka bir deyişle, isteğe bağlı bir durdurma Turing makinesini verebilseniz de, "düzgün bir şekilde" çalışacak şekilde size_t uzunluğunda bir uzunluk seçin, tüm durdurma Turing makinelerinin düzgün çalışacağı şekilde bir boy_t uzunluğu seçmenin bir yolu yoktur)

Öyleyse: C99 Turing tamamlandı mı?


3
C'nin "soyut anlambilimi" nedir? Herhangi bir yerde tanımlanmış mı?
Yuval Filmus,

4
@YuvalFilmus - Örn, buraya bakınız , örn. "Bu nasıl gcc yapıyor?" İfadesinin aksine standartta tanımlandığı şekilde C.
TLW

1
modern bilgisayarların TM gibi sonsuz belleğe sahip olmadıkları ancak “evrensel bilgisayarlar” olarak kabul edildikleri "tekniktir". ve "dilbilimleri" nde programlama dillerinin tüm uygulamaların elbette bellekte sınırlı olması dışında, sonlu bir bellek almadıklarını not edin . bkz. örneğin , bilgisayarımız bir Turing makinesi olarak çalışıyor . Her neyse, esas olarak, tüm "ana" programlama dilleri tamamlanıyor.
vzn

2
C (Turing makineleri gibi) hesaplamaları için dahili bilgisayar belleğini kullanmakla sınırlı değildir, bu yüzden bu gerçekten C'nin Turing'in eksiksizliğine karşı geçerli bir argüman değildir
reinierpost

@reinierpost - bu bir teletype sapient demek gibi. "C + harici bir TM eşdeğeri" demek Turing tamamlandı, C Turing tamamlandı değil.
TLW

Yanıtlar:


34

Emin değilim ama bence cevabın hayır, ince sebeplerden dolayı hayır. Ben birkaç yıl önce Teorik Bilgisayar Bilimi üzerine sorulan ve burada sunacağız ötesine geçer bir cevap alamadım.

Çoğu programlama dilinde, bir Turing makinesini aşağıdaki şekillerde simüle edebilirsiniz:

  • sonlu otomatın sonlu miktarda bellek kullanan bir program ile simüle edilmesi;
  • bandın, mevcut pozisyondan önce ve sonra bandın içeriğini temsil eden bir çift tamsayı listesi ile simüle edilmesi. İşaretçiyi hareket ettirmek, listelerden birinin kafasını diğer listeye aktarmak anlamına gelir.

Bilgisayarda çalışan somut bir uygulamada, kaset çok uzarsa bellek yetersiz kalır, ancak ideal bir uygulama Turing makine programını güvenilir bir şekilde uygulayabilir. Bu, kalem ve kağıtla veya daha fazla belleğe sahip bir bilgisayar ve her kelime için daha fazla bit içeren bir mimariyi hedefleyen bir derleyici veya programın belleği tükenirse yapılabilir.

Bu, C'de çalışmaz, çünkü sonsuza dek uzayabilecek bağlantılı bir listeye sahip olmak imkansızdır: Düğüm sayısında her zaman bir sınır vardır.

Nedenini açıklamak için önce bir C uygulamasının ne olduğunu açıklamaya ihtiyacım var. C aslında programlama dilleri ailesidir. ISO C standardı sözdizimi ve semantik programlama dilleri ailesi (İngilizce izin verdiğini formalite düzeyiyle) (bu standardın daha doğrusu, belirli bir versiyonu) tanımlar. C bir çok tanımsız davranışa ve uygulama tanımlı davranışa sahiptir.. C'nin bir “uygulaması”, uygulama tarafından tanımlanan tüm davranışı kodlar (kodlanacak şeylerin listesi C99 için Ek J'de bulunur). C'nin her uygulaması ayrı bir programlama dilidir. “Uygulama” kelimesinin anlamının biraz tuhaf olduğuna dikkat edin: gerçekte ne anlama geldiği bir dil çeşididir, aynı dil değişkenini uygulayan birden fazla farklı derleyici program olabilir.

2CHAR_BITt2CHAR_BIT×sizeof(t)

2CHAR_BIT×sizeof(void*)

Değerleri CHAR_BITve sizeof(void*)gözlemlenebilir olması nedeniyle hafızanız tükenirse, bu parametreler için programınızı daha büyük değerlerle çalıştırmaya devam edemezsiniz. Programı farklı bir programlama dili altında çalıştırıyor olacaksınız - farklı bir C uygulaması.

n×2CHAR_BIT×sizeof(void*)n

C doğrudan maksimum özyineleme derinliği empoze etmez. Bir uygulamanın en fazlaya sahip olmasına izin verilir, ancak bir uygulamanın da olmamasına izin verilir. Fakat bir işlev çağrısı ile ebeveyni arasında nasıl iletişim kurarız? Argümanları adreslenebilirlerse iyi değildir, çünkü dolaylı olarak özyinelemenin derinliğini sınırlar: çünkü bir işleve sahipseniz , aktif karelerdeki int f(int x) { … f(…) …}tüm oluşumların kendi adresleri vardır ve bu nedenle iç içe aramaların sayısı numaralarla sınırlandırılır. olası adreslerden .xfx

AC programı adreslenemeyen depolamayı registerdeğişkenler halinde kullanabilir. “Normal” uygulamalar, adresi olmayan az sayıda, sonlu sayıda değişkene sahip olabilir, ancak teoride bir uygulama sınırsız miktarda registerdepolamaya izin verebilir . Böyle bir uygulamada, bir fonksiyona sınırsız sayıda özyinelemeli çağrı yapabilirsiniz, argümanı olduğu sürece register. Ancak argümanlar olduğu için register, onlara bir işaretçi yapamazsınız ve bu yüzden verilerini açıkça kopyalamanız gerekir: işaretçilerden oluşan rastgele boyutta bir veri yapısını değil, yalnızca sınırlı miktarda veriyi geçirebilirsiniz.

Sınırlandırılmamış özyineleme derinliği ve bir işlevin yalnızca doğrudan arayandan ( registerargümanlar) veri alabilmesi ve verileri doğrudan arayana (geri dönüş değeri) geri getirebilmesi kısıtlamasıyla , deterministik aşağı itme otomatlarının gücünü elde edersiniz .

Daha ileri gitmek için bir yol bulamıyorum.

(Elbette, programın kaset içeriğini, dosya giriş / çıkış işlevleriyle harici olarak depolamasını sağlayabilirsiniz. Fakat daha sonra C'nin Turing tamamlandı mı, C artı sonsuz bir depolama sisteminin Turing tamamlandı mı olduğunu sormazsınız. Bu sorunun cevabı sıkıcı bir “evet” dır. Depolamayı bir Turing kahramanı - çağrı fopen("oracle", "r+"), fwritebuna ilk bant içeriği olarak tanımlayabilir  freadve son bant içeriğini geri alabilirsiniz.)


Adres setinin neden sonlu olması gerektiği hemen belli değil. Bağladığınız
Alexey B.

4
Üzgünüz, ama aynı mantıkla, Turing-complete programlama dili hiç yok. Her dilin, adres alanında açık veya kapalı bir sınırlaması vardır. Sonsuz hafızalı bir makine oluşturursanız, rasgele erişim işaretçileri de sonsuz uzunlukta olacaktır. Bu nedenle, eğer böyle bir makine belirirse, yüksek seviyeli diller için bir API ile birlikte sıralı hafızaya erişim için bir komut sunmak zorunda kalacaktır.
IMil

14
@IMil Bu doğru değil. Bazı programlama dilleri, adres alanında sınırlı değildir, hatta dolaylı olarak bile. Açık bir örnek vermek gerekirse, kasetin başlangıç ​​durumunun programı oluşturduğu evrensel bir Turing makinesi, Turing-complete bir programlama dilidir. Aslında pratikte kullanılan birçok programlama dili aynı özelliğe sahiptir, örneğin Lisp ve SML. Bir dil “rasgele erişim işaretçisi” kavramına sahip olmak zorunda değildir. (devam)
Gilles 'ÇÖZME'

11
@IMil (devamı) Uygulamalar genellikle performans için çalışır, ancak belirli bir bilgisayarda çalışan bir uygulamanın bilgisayar belleğinin boyutuna bağlı olduğundan Turing-complete olmadığını biliyoruz. Ancak bu, uygulamanın tüm dili değil, yalnızca bir alt kümesini (N bayt hafızasında çalışan programlar) uyguladığı anlamına gelir. Programı bir bilgisayarda çalıştırabilirsiniz ve belleği yetersizse, daha büyük bir bilgisayara taşıyın ve bu şekilde sonsuza kadar veya duruncaya kadar devam edin. Bu, tüm dili uygulamak için geçerli bir yol olacaktır.
Gilles 'SO- kötülük olmayı bırak'

6

C99'un va_copyvariadik argüman API'sine eklenmesi bize Turing'in bütünlüğüne bir arka kapı verebilir. Değişken argümanlar listesi boyunca bir kereden fazla tekrarlamak mümkün hale geldiğinden, argümanları ilk va_argsalandan başka bir fonksiyonda, göstergesiz bir gösterici uygulamak için kullanılabilir.

Tabii ki, varyadik argüman API'sinin gerçek bir uygulaması muhtemelen bir yerlerde bir göstericiye sahip olacak, ancak soyut makinemizde bunun yerine büyü kullanarak uygulanabilir.

İşte isteğe bağlı geçiş kuralları ile 2 yığın aşağı itme otomatiği uygulayan bir demo:

#include <stdarg.h>
typedef struct { va_list va; } wrapped_stack; // Struct wrapper needed if va_list is an array type.
#define NUM_SYMBOLS /* ... */
#define NUM_STATES /* ... */
typedef enum { NOP, POP1, POP2, PUSH1, PUSH2 } operation_type;
typedef struct { int next_state; operation_type optype; int opsymbol; } transition;
transition transition_table[NUM_STATES][NUM_SYMBOLS][NUM_SYMBOLS] = { /* ... */ };

void step(int state, va_list stack1, va_list stack2);
void push1(va_list stack2, int next_state, ...) {
    va_list stack1;
    va_start(stack1, next_state);
    step(next_state, stack1, stack2);
}
void push2(va_list stack1, int next_state, ...) {
    va_list stack2;
    va_start(stack2, next_state);
    step(next_state, stack1, stack2);
}
void step(int state, va_list stack1, va_list stack2) {
    va_list stack1_copy, stack2_copy;
    va_copy(stack1_copy, stack1); va_copy(stack2_copy, stack2);
    int symbol1 = va_arg(stack1_copy, int), symbol2 = va_arg(stack2_copy, int);
    transition tr = transition_table[state][symbol1][symbol2];
    wrapped_stack ws;
    switch(tr.optype) {
        case NOP: step(tr.next_state, stack1, stack2);
        // Note: attempting to pop the stack's bottom value results in undefined behavior.
        case POP1: ws = va_arg(stack1_copy, wrapped_stack); step(tr.next_state, ws.va, stack2);
        case POP2: ws = va_arg(stack2_copy, wrapped_stack); step(tr.next_state, stack1, ws.va);
        case PUSH1: va_copy(ws.va, stack1); push1(stack2, tr.next_state, tr.opsymbol, ws);
        case PUSH2: va_copy(ws.va, stack2); push2(stack1, tr.next_state, tr.opsymbol, ws);
    }
}
void start_helper1(va_list stack1, int dummy, ...) {
    va_list stack2;
    va_start(stack2, dummy);
    step(0, stack1, stack2);
}
void start_helper0(int dummy, ...) {
    va_list stack1;
    va_start(stack1, dummy);
    start_helper1(stack1, 0, 0);
}
// Begin execution in state 0 with each stack initialized to {0}
void start() {
    start_helper0(0, 0);
}

Not: Bir va_listdizi tipi ise, fonksiyonlarda gizli işaretçi parametreleri vardır. Bu nedenle, muhtemelen tüm va_listargümanların türlerini değiştirmek daha iyi olacaktır wrapped_stack.


Bu işe yarayabilir. Muhtemel bir endişe, sınırsız sayıda otomatik va_listdeğişken tahsis etmeye dayanmasıdır stack. Bu değişkenlerin bir adresi olmalı &stackve bunlardan yalnızca sınırlı sayıda alabiliriz. Bu gereklilik registerbelki de her yerel değişkeni bildirerek çözülebilir.
chi

@chi AIUI, birileri adresi almaya çalışmadığı sürece, bir adrese sahip olmak için bir değişken gerekli değildir. Ayrıca, üç noktadan hemen önceki argümanı açıklamak yasadışıdır register.
feersum

Aynı mantıkla, intbir kişi sınırı kullanmadıkça bir sınırın olması gerekmemeli sizeof(int)midir?
Chi

@chi Hiç de değil. Soyut semantik bir parçası olarak tanımlamış intsonlu sınırları arasında bir değere sahiptir INT_MINve INT_MAX. Ve bir inttaşma değeri bu sınırları aşarsa, tanımsız davranış oluşur. Diğer taraftan, standart kasıtlı olarak tüm nesnelerin fiziksel olarak belirli bir adreste bellekte bulunmasını gerektirmez, çünkü bu, nesneyi bir kayıt defterinde saklamak, nesnenin yalnızca bir bölümünü depolamak, onu standarttan farklı bir şekilde temsil etmek gibi optimizasyonlara izin verir. mizanpaj veya gerekmediğinde tamamen yok saymak.
feersum

4

Standart olmayan aritmetik, belki?

Öyleyse, sorunun sınırlı boyutta olduğu görülüyor sizeof(t). Ancak, etrafta bir çalışma bildiğimi düşünüyorum.

Bildiğim kadarıyla, C, tamsayı türü için standart tamsayıları kullanmak için bir uygulama gerektirmiyor. Bu nedenle standart dışı bir aritmetik modeli kullanabiliriz . Sonra sizeof(t)standart olmayan bir sayıya ayarlanırdık ve şimdi ona sınırlı sayıda adımda asla ulaşamayacağız. Bu nedenle, Turing makineleri bandının uzunluğu her zaman "maksimum" değerinden daha az olacaktır, çünkü maksimum değere tam anlamıyla ulaşmak imkansızdır. sizeof(t)basitçe kelimenin normal anlamındaki bir sayı değildir.

Bu elbette bir tekniktir: Tennenbaum teoremi . Peano aritmetiğinin tek modelinin, kesinlikle yapamayacağı standart olan olduğunu belirtir. Ancak, bildiğim kadarıyla, C, Peano aksiyomlarını karşılayan veri tiplerini kullanmak için uygulamalar gerektirmez ve uygulamanın da hesaplanabilir olmasını gerektirmez, bu yüzden bu bir sorun olmamalıdır.

Standart olmayan bir tamsayı çıkarmaya çalışırsanız ne olmalıdır? Standart olmayan bir dize kullanarak standart olmayan herhangi bir tamsayıyı temsil edebilirsiniz, bu nedenle yalnızca o dizenin önünden basamağı yayınlayın.


Bir uygulama neye benzer?
reinierpost

@ reinierpost Sanırım bazı standart dışı PA modellerini kullanarak verileri temsil edeceğini tahmin ediyorum. PA derecesi kullanarak aritmetik işlemleri hesaplardı . Bence böyle bir model geçerli bir C uygulaması sağlamalıdır.
PyRulez

Üzgünüm, bu işe yaramıyor. sizeof(t)kendisi bir tür değeridir size_t, bu nedenle 0 ile 0 arasında doğal bir tam sayıdır SIZE_MAX.
Gilles 'SO- kötülük yapmayı bırak'

@Gilles SIZE_MAX, o zamanlar da standart dışı bir doğal olurdu.
PyRulez

Bu ilginç bir yaklaşım. Ayrıca standart dışı olması için örneğin intptr_t / uintptr_t / ptrdiff_t / intmax_t / uintmax_t gerektiğine dikkat edin. C ++ bu değil emin C. hakkında ... ileri ilerleme teminat uyulmadığını olur
TLW

0

IMO, güçlü bir sınırlama adreslenebilir alanın (imleç boyutu ile) sonlu olmasıdır ve bu kurtarılamaz.

Biri, hafızanın "diske değiştirilebilir" olduğunu savunabilir, ancak bir noktada adres bilgileri, adreslenebilir boyutu aşacaktır.


Bu kabul edilen cevabın ana noktası değil mi? Bu 2016 sorunun cevabına yeni bir şey ekleyebileceğini sanmıyorum.
Chi

@chi: hayır, kabul edilen cevap, geçici bir çözüm olduğuna inanılan harici hafızaya geçmekten bahsetmiyor.
Yves Daoust

-1

Uygulamada, bu kısıtlamalar Turing'in eksiksizliği ile ilgili değildir. Asıl gereksinim, kasetin keyfi değil uzun sürmesine izin vermektir. Bu, farklı türde bir durma problemi yaratacaktır (evren, kaseti nasıl "hesaplar"?)

"Python Turing tam değil çünkü bir listeyi sonsuz büyük yapamazsınız" demek kadar sahte.

[Düzenleme: nasıl düzenleyeceğinizi açıklayan Bay Whitledge'e teşekkürler.]


7
Bunun soruya cevap verdiğini sanmıyorum. Soru zaten bu cevabı öngördü ve bunun neden geçerli olmadığını açıkladı: "C standardı size_t'nin keyfi olarak büyük olmasına izin vermesine rağmen, belli bir uzunlukta sabitlenmeli ve ne kadar uzunlukta olursa olsun hala sınırlı olmalı ". Bu argümana herhangi bir cevabınız var mı? Cevap, bu argümanın neden yanlış (veya doğru) olmadığını açıklamadıkça soruyu cevaplanmış olarak sayabileceğimizi sanmıyorum.
DW

5
Herhangi bir zamanda, bir tür size_tdeğeri sonludur. Sorun, size_thesaplama boyunca geçerli olan bir sınır oluşturamazsınız : herhangi bir sınır için, bir program taşabilir. Ancak C dili, bunun için bir sınır olduğunu belirtir size_t: verilen bir uygulamada, yalnızca sizeof(size_t)baytlara kadar büyüyebilir . Ayrıca, iyi ol . Sizi eleştiren kişilerin “kendi başlarına düşünemeyeceklerini” söylemek kaba bir şey.
Gilles 'SO- kötülükten

1
Bu doğru cevap. Tornalama makinesi sonsuz bir bant gerektirmez, “keyfi bir şekilde uzun” bir bant gerektirir. Yani, bandın hesaplamayı tamamlamak zorunda olması gerektiği sürece olduğunu varsayabilirsiniz. Ayrıca, bilgisayarınızın ihtiyaç duyduğu kadar belleğe sahip olduğunu varsayabilirsiniz. Sonsuz bir zamanda kesinlikle gerekli değildir, çünkü sonlu bir süre içinde duran hesaplamalar muhtemelen sonsuz miktarda bant kullanamaz.
Jeffrey L Whitledge,

Bu cevabın gösterdiği şey, her TM için, simüle etmek için yeterli işaretçi uzunluğuna sahip bir C uygulaması yazabileceğinizdir. Bununla birlikte, herhangi bir TM'yi simüle edebilecek bir C uygulaması yazmak mümkün değildir . Dolayısıyla şartname, herhangi bir uygulamanın T-tamamlanmış olduğunu yasaklamaktadır. İşaretçi uzunluğu sabittir, çünkü kendisi de T-tamam değildir.

1
Bu, bu topluluktaki bireylerin çoğunun yetersizliği nedeniyle neredeyse hiç görülmeyen bir başka doğru cevaptır. Bu arada, kabul edilen cevap yanlıştır ve yorum bölümü eleştirel yorumları silen moderatörler tarafından korunmaktadır. Güle güle, cs.stackexchange.
xamid

-1

Çıkarılabilir medya, sınırlandırılmamış bellek sorununu aşmamızı sağlar. Belki de insanlar bunun bir istismar olduğunu düşüneceklerdir, ama bence bu tamam ve yine de kaçınılmazdır.

Evrensel bir Turing makinesinin herhangi bir uygulamasını düzeltin. Bant için çıkarılabilir medya kullanıyoruz. Kafa, geçerli diskin sonundan veya başlangıcından geçtiğinde, makine kullanıcıdan bir önceki veya bir sonrakiini yerleştirmesini ister. Simüle edilen bandın sol ucunu belirtmek için özel bir işaretleyici kullanabiliriz veya her iki yönde de sınırlandırılmamış bir bant olabilir.

Buradaki kilit nokta, C programının yapması gereken her şeyin sonlu olmasıdır. Bilgisayar yalnızca otomatiği simüle etmek için yeterli belleğe ihtiyaç duyar ve size_tyalnızca (aslında oldukça küçük) miktarda bellek ve herhangi bir sabit sonlu boyutta olabilen disklerde adreslenmesini sağlayacak kadar büyük olmalıdır. Kullanıcının yalnızca bir sonraki veya önceki diski yerleştirmesi istendiğinden, "Lütfen 123456 numaralı diskleri yerleştirin ..." diyen sınırsız büyük tam sayılara ihtiyacımız yok.

Sanırım ana itiraz, kullanıcının katılımı olabilir, ancak bu, herhangi bir uygulamada kaçınılmaz görünüyor, çünkü sınırsız hafızayı uygulamanın başka bir yolu yok gibi görünüyor.


3
C tanımı, sınırlandırılmamış harici depolama gerektirmediği takdirde, bunun Turing'in eksiksiz olduğunun bir kanıtı olarak kabul edilemeyeceğini savunuyorum. (ISO 9899 elbette gerçek dünya mühendisliği için yazılmasını gerektirmez.) Beni ilgilendiren şey, eğer bunu kabul edersek, benzer bir nedenden dolayı DFA'ların tamamlandıklarını iddia edebiliriz, kullanılabileceğinden bir bandı bant üzerinde sürmek için (harici depolama birimi).
chi

@chi DFA argümanının nasıl takip ettiğini anlamıyorum. Bir DFA'nın amacı, yalnızca depolamaya okuma erişimine sahip olmasıdır. "Bir bandı bant üzerinde sürmesine" izin veriyorsanız, bu tam olarak bir Turing makinesi değil mi?
David Richerby

2
Gerçekten, burada birazcık nitpicking. Mesele şu ki: C'ye bir "kaset" eklemek neden C'nin bir DFA'yı taklit etmesine izin veriyor ve bu gerçeği DFA'lara aynı şeyi yapamadığımızda C'nin Tamamlandığını iddia etmek için kullanalım? C sınırsız belleği kendi başına uygulamak için bir yol yoksa, Turing tamamlandı olarak kabul edilmemelidir. (Ben hala buna "ahlaki açıdan" derdim, en azından tamamlandı, çünkü sınırlar o kadar büyüktü ki pratikte çoğu durumda farketmezlerdi) Bence konuyu kesin olarak çözmek için, kişinin çok katı bir resmi şartnameye ihtiyacı olacağını düşünüyorum C (ISO standardı yeterli değil)
chi

1
@chi Tamam, çünkü C dosya G / Ç yordamlarını içerir. DFA'lar yapmaz.
David Richerby

1
C bu rutinlerin ne yaptığını tam olarak belirtmemektedir - anlambilimlerinin çoğu uygulama tanımlanmıştır. Örneğin, dosya içeriğini saklamak için AC uygulaması gerekli değildir, örneğin, her dosyanın "/ dev / null" olduğu gibi davranabileceğini düşünüyorum. Sınırsız miktarda veri depolamak da gerekmez. C uygulamalarının büyük çoğunluğunun ne yaptığını düşünürken ve bu davranışı ideal bir makineye genelleştirirken, argümanınızın doğru olduğunu söyleyebilirim. C tanımına kesinlikle güvenirsek, sadece uygulamayı unutarak, bunun geçerli olduğunu sanmıyorum.
chi

-2

size_tSonsuz büyük olmayı seçin

size_tSonsuz büyük olmayı seçebilirsin . Doğal olarak, böyle bir uygulamanın gerçekleştirilmesi mümkün değildir. Ama içinde yaşadığımız dünyanın sonlu doğası göz önüne alındığında bu sürpriz değil.

Pratik Uygulamalar

Ancak böyle bir uygulamanın gerçekleştirilmesi mümkün olsa bile, pratik konular olacaktır. Aşağıdaki C ifadesini göz önünde bulundurun:

printf("%zu\n",SIZE_MAX);

SIZE_MAXSIZE_MAXO(2size_t)size_tSIZE_MAXprintf

Neyse ki, teorik amaçlarımız için, spesifikasyonda garantilerin printftüm girdiler için sonlandırılacağı herhangi bir şart bulamadım . Bu yüzden, bildiğim kadarıyla, burada C spesifikasyonunu ihlal etmiyoruz.

Hesaplamalı Tamamlanma Üzerine

Teorik uygulamamızın Turing Complete olduğunu kanıtlamak için hala devam etmektedir . Bunu "herhangi bir tek bantlı Turing Makinesi" uygulayarak gösterebiliriz.

Birçoğumuz muhtemelen bir Turing Makinesini okul projesi olarak uyguladık. Belirli bir uygulamanın ayrıntılarını vermeyeceğim, ancak işte sık kullanılan bir strateji:

  • Herhangi bir makine için durum sayısı, simge sayısı ve durum geçiş tablosu belirlenir. Böylece durumları ve sembolleri sayı olarak, durum geçiş tablosunu 2 boyutlu bir dizi olarak temsil edebiliriz.
  • Bant, bağlı bir liste olarak gösterilebilir. Tek bir çift bağlantılı liste veya iki tek bağlantılı liste kullanabiliriz (geçerli konumdan her yön için bir tane).

Şimdi böyle bir uygulamayı gerçekleştirmek için neyin gerekli olduğunu görelim:

  • Bazı sabit ancak keyfi büyük sayılar kümesi gösterme yeteneği. Herhangi bir rasgele sayıyı temsil etmek için, MAX_INTsonsuz olmayı da seçeriz. (Alternatif olarak, durumları ve simgeleri temsil etmek için diğer nesneleri kullanabiliriz.)
  • Kasetimiz için isteğe bağlı olarak bağlantılı bir liste oluşturma yeteneği. Bir kez daha, boyutta sonlu bir sınır yoktur. Bu, kaseti oluşturmak için sonsuza dek harcayacağımız bu listeyi önceden oluşturamayacağımız anlamına gelir. Ancak, dinamik bellek ayırmayı kullanırsak, bu listeyi aşamalı olarak oluşturabiliriz. Kullanabiliriz malloc, ancak dikkate almamız gereken daha çok şey var:
    • C spesifikasyonu malloc, örneğin mevcut hafıza tükendiğinde başarısız olmasına izin verir . Bu nedenle, uygulamamız mallochiçbir zaman başarısız olmazsa gerçekten evrenseldir .
    • Bununla birlikte, uygulamamız sonsuz hafızalı bir makinede çalıştırılıyorsa, arızaya gerek yoktur malloc. C standardını ihlal etmeden, uygulamamızın mallocasla başarısız olmayacağının garantisi olacak.
  • İşaretçileri kaldırma, dizi öğelerini arama ve bağlı liste düğüm üyelerine erişme yeteneği.

Bu yüzden yukarıdaki liste, varsayımsal C uygulamamızda bir Turing Makinesi uygulamak için gerekli olan şeydir. Bu özellikler sonlandırılmalıdır. Bununla birlikte, başka herhangi bir şeyin sonlandırılmasına izin verilmeyebilir (standart tarafından istenmedikçe). Bu aritmetik, IO vb. İçerir.


6
printf("%zu\n",SIZE_MAX);Böyle bir uygulamaya ne yazdıracak?
Ruslan

1
@Ruslan, böyle bir uygulama imkansızdır, tıpkı bir Turing makinesi uygulamak imkansız olduğu gibi. Ancak böyle bir uygulama mümkün olsaydı, sonsuz sayıda sayının - muhtemelen büyük olasılıkla, ondalık basamakların sonsuz bir akışının bazı ondalık gösterimlerini basacağını hayal ediyorum.
Nathan Davis

2
@ NathanDavis Bir Turing makinesi uygulamak mümkündür. İşin püf noktası sonsuz bir kaset oluşturmanıza gerek kalmamasıdır - kasetin kullanılmış kısmını gerektiği gibi kademeli olarak oluşturursunuz.
Gilles 'SO- kötülükten

2
@Gilles: İçinde yaşadığımız bu sonlu evrende, bir Turing makinesi uygulamak imkansızdır.
gnasher729

1
@ NathanDavis Ama eğer bunu yaparsanız, o zaman değiştiniz sizeof(size_t)(veya CHAR_BITS). Yeni durumdan devam edemezsiniz, tekrar başlamak zorundasınız, ancak programın yürütülmesi şimdi bu sabitlerin farklı olduğu için farklı olabilir
Gilles 'SO' kötü

-2

Buradaki ana argüman, size_t boyutunun sonlu olduğu, ancak sınırsız olmasına rağmen.

Bunun için bir geçici çözüm var, ancak bunun ISO C ile çakışıp çakışmadığından emin değilim.

Sonsuz hafızalı bir makineniz olduğunu varsayalım. Bu nedenle, işaretçi boyutuna bağlı değilsiniz. Hala size_t türünüz var. Bana sizeof (size_t) ne olduğunu sorarsanız, cevap sadece sizeof (size_t) olacaktır. Örneğin 100'den büyük olup olmadığını sorarsanız cevap evet. Sizeof (size_t) / 2'nin ne olduğunu sorarsanız, cevabın hala sizeof (size_t) olduğunu tahmin edebilirsiniz. Yazdırmak istiyorsanız, bazı çıktılar üzerinde anlaşabiliriz. Bu ikisinin farkı NaN ve benzeri olabilir.

Özet, size_t koşulunun sonlu bir boyuta getirilmesinin gevşetilmesinin halihazırda mevcut olan herhangi bir programı bozmayacağı yönünde.

PS Bellek ayırma sizeof (size_t) hala mümkün, sadece sayılabilen bir boyuta ihtiyacınız var, o yüzden diyelim ki tüm hayvanları (veya benzeri bir hile).


1
"Bu ikisinin farkı NaN olabilir". Hayır olamaz. C de tamsayı tipinde bir NaN diye bir şey yoktur
TLW 24/16

Standarda göre, sizeofbir dönmek zorunda size_t. Bu yüzden belirli bir değer seçmek zorundasınız.
Draconis

-4

Evet öyle.

1. Alıntılanan cevap

Benim (ve diğer) doğru cevaplarımdaki yüksek miktardaki düşürmelere tepki olarak - yanlış cevapların endişe verici derecede yüksek onayına kıyasla - daha az teorik olarak daha derin bir alternatif açıklama aradım. Bulduğum bu bir. Umarım, buradaki ortak hatalardan bazılarını kapsar, böylece biraz daha fazla fikir edinilir. Argümanın temel kısmı:

[...] Argümanı şöyle: Birinin, yürütülmesi sırasında keyfi bir miktar depolamaya kadar gerektirebilecek belirli bir sonlandırma programı yazdığını varsayalım. Bu programı değiştirmeden, bir bilgisayar donanım parçası ve bu hesaplamayı yapmak için yeterli depolama sağlayan C derleyicisini uygulamak mümkündür. Bu, char (CHAR_BITS aracılığıyla) ve / veya işaretçilerin (size_t aracılığıyla) genişliğinin genişletilmesini gerektirebilir, ancak programın değiştirilmesi gerekmeyebilir. Bu mümkün olduğu için, C programları sonlandırmak için gerçekten Turing-Complete'tir.

Bu tartışmanın en zor yanı, yalnızca programları sonlandırmayı düşünürken işe yaramasıdır. Sonlandırma programları, depolama gereksinimlerine bağlı statik bir üste sahip olmaları ve bu programın, "uygun" olana kadar artan depolama boyutlarına sahip olan programı istenen girdi üzerinde çalıştırarak deneysel olarak belirleyebilecekleri bu hoş özelliğe sahiptir.

Düşüncelerim treninde yanılmamamın nedeni, “faydalı” sonlandırmayan programların daha geniş bir sınıfını göz önünde bulundurmamdı [...]

Kısacası, her hesaplanabilir fonksiyon için C dilinde bir çözüm olduğundan (sınırsız üst sınırlardan dolayı), her hesaplanabilir sorunun bir C programı vardır, bu nedenle C Turing-complete olur.

2. Orijinal cevabım

Teorik bilgisayar bilimlerindeki matematiksel kavramlar (Turing-eksiksizlik gibi) ile gerçek dünya uygulamaları arasında pratik bilgisayar bilimindeki teknikler arasında yaygın karışıklıklar vardır. Turing-eksiksizliği, fiziksel olarak var olan makinelerin veya uzaydaki sınırlı modellerin bir özelliği değildir. Bu sadece matematiksel teorilerin özelliklerini tanımlayan soyut bir nesnedir.

C99, uygulama temelli kısıtlamalara bakılmaksızın, tıpkı diğer tüm programlama dillerinde olduğu gibi, tamamen işlevseldir, çünkü işlevsel olarak eksiksiz bir mantıksal bağlayıcılar setini ifade edebilir ve prensip olarak sınırsız miktarda belleğe erişebilir. İnsanlar C açıkça sınırlı olmak üzere rastgele bellek erişimi kısıtlaması sivri out var ama Turing-tamlık iken bu C standardında ayrıca belirtilen kısıtlamalar, çünkü bu, hiçbir şey tek could hileli değil onlar olmadan gerektirdiği zaten:

İşte yapıcı olmayan kanıt için yeterli olması gereken mantıksal sistemler hakkında çok temel bir şey . Bazı aksiyom şemalarına ve kurallarına sahip bir hesabı düşünün, öyle ki mantıksal sonuç kümesi X olur. Şimdi, eğer bazı kurallar veya aksiyomlar eklerseniz, mantıksal sonuçların sayısı artar, yani X'in bir süperseti olması gerekir. , modal mantık S4 S5'te düzgün bir şekilde bulunur. Benzer şekilde, Turing tamamlandı olan bir alt belirtime sahipseniz, ancak üstüne bazı kısıtlamalar eklerseniz, bunlar X'teki sonuçların hiçbirini engellemez, yani tüm kısıtlamaları aşmanın bir yolu olmalıdır. Turing tamamlanmamış bir dil istiyorsanız, hesap azaltılmalı, uzatılmamalıdır. Bir şeyin mümkün olmayacağını iddia eden uzantılar, ancak gerçekte, sadece tutarsızlık katar. C standardındaki bu tutarsızlıklar, Turing bütünlüğünün pratik uygulama ile ilgisi olmadığı gibi pratik bir sonuç vermeyebilir.

Özyineleme derinliğine dayalı rasgele sayıları simüle etme (yani, bu ; programlama / sözde-başlıklar yoluyla çoklu sayıları destekleme imkanı; C'de özyineleme derinliği için teorik bir sınır yoktur ) veya sınırsız program hafızasını simüle etmek için dosya depolamasını kullanmaktır ( fikir ). muhtemelen sonsuz olasılıklar sadece iki takım için yapıcı C99 Turing-tamlığı kanıtlamak. Kişi, hesaplanabilirlik için zaman ve mekan karmaşıklığının konu dışı olduğunu hatırlamalıdır. Özellikle, Turing bütünlüğünü tahrif etmek için sınırlı bir ortam varsaymak, sadece sınırlamadır çünkü bu sınırlama, öngörülen karmaşıklığı aşan tüm sorunları dışlar.

( NOT : Bu cevabı sadece insanların bir tür uygulamaya yönelik sınırlı düşünceden dolayı matematiksel sezgiler kazanmalarını engellemek için yazdım. Çoğu öğrencinin yanlış kabul edilen cevapları dikkate alarak yanlış kabul ettiği cevabını okuyacak olması üzücü. temel akıl yürütme kusurları, böylece daha fazla insan bu tür yanlış inançları yayacak. Bu cevabı reddederseniz, sorunun sadece bir parçasısınız.)


4
Son paragrafını takip etmiyorum. Kısıtlamalar eklemenin anlamlı gücü arttırdığını iddia ediyorsunuz, ancak bu kesinlikle doğru değil. Kısıtlamalar yalnızca ifade gücünü azaltabilir. Örneğin, C'yi alırsanız ve hiçbir programın 640kb'dan daha fazla depolamaya erişemeyeceği (herhangi bir türde) kısıtlama eklerseniz, bunu açıkça Turing-tamamlanmayan süslü bir sonlu otomatize dönüştürdünüz.
David Richerby

3
Sabit bir depolama alanınız varsa, sahip olduğunuzdan daha fazla kaynak gerektiren hiçbir şeyi simüle edemezsiniz. Hafızanızın içinde olabileceği sadece çok sayıda yapılandırma vardır, bu da yapabileceğiniz sadece çok sayıda şey olduğu anlamına gelir.
David Richerby

2
Neden "fiziksel olarak var olan makinelere" atıfta bulunduğunu anlamıyorum. Turing bütünlüğünün, fiziksel sistemlerin değil, matematiksel bir hesaplama modelinin bir özelliği olduğunu unutmayın. Sonlu bir nesne olarak hiçbir fiziksel sistemin Turing makinelerinin gücüne yakın olamayacağı konusunda hemfikirdim, ama bu konu dışı. Hala herhangi bir programlama dilini alabilir, anlambiliminin matematiksel tanımını düşünebilir ve bu matematiksel nesnenin Turing'in tamamlanıp tamamlanmadığını kontrol edebiliriz. Conway'in yaşam oyunu, fiziksel bir uygulama olmasa bile, güçlü olmaktır.
Chi

2
@xamid Bu sitenin denetim politikalarına ilişkin endişeleriniz varsa, Computer Science Meta'ya götürün . O zamana kadar lütfen nazik ol . Başkalarının sözlü tacizi hoş görülmeyecektir. (Elimizdeki konuyla ilgili olmayan tüm yorumları kaldırdım.)
Raphael

2
Bir işaretçinin genişliğini değiştirmenin programı değiştirmeyeceğini söylersiniz, ancak programlar işaretçinin genişliğini okuyabilir ve bu değerle istediklerini yapabilir. İçin aynı CHAR_BITS.
Draconis
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.