*(>:^]*(*>{<-!<:^>[:((-<)<(<!-)>>-_)_<<]>:]<]]}*<)]*(:)*=<*)>]
-ln
Komut satırı bayrakları ile çalıştırılması gerekiyor (bu nedenle +4 bayt). 0
Kompozit sayılar ve 1
primerler için yazdırır .
Çevrimiçi deneyin!
Bunun önemsiz olmayan ilk Stack Cats programı olduğunu düşünüyorum.
açıklama
Hızlı bir Stack Cats tanıtımı:
- Yığın Kedileri, geçerli bir yığına işaret eden bir bant başlığıyla, sonsuz bir yığın bantta çalışır. Her yığın başlangıçta sonsuz miktarda sıfır ile doldurulur. İfadelerimde bu sıfırları genellikle görmezden geleceğim, bu yüzden "yığının dibinde" derken sıfır olmayan en düşük değeri ve "yığın boş" dediğimde üzerinde yalnızca sıfır olduğunu kastediyorum.
- Program başlamadan önce,
-1
ilk istifin üzerine a itilir ve ardından girişin tamamı buna itilir. Bu durumda, -n
bayrak nedeniyle giriş, ondalık bir tamsayı olarak okunur.
- Programın sonunda, mevcut yığın çıktı için kullanılır.
-1
Dipte bir varsa , yoksayılır. Yine, -n
bayrak nedeniyle , yığından gelen değerler basitçe satır besleme ayrılmış ondalık tam sayılar olarak yazdırılır.
- Stack Cats geri dönüşümlü bir program dilidir: her kod parçası geri alınabilir (Stack Cats açık bir geçmişi takip etmeden). Daha spesifik olarak, kodun herhangi bir parçasını tersine çevirmek için, sadece o ayna, örneğin
<<(\-_)
olur (_-/)>>
. Bu tasarım hedefi, dilde ne tür operatörler ve kontrol akışı yapıları olduğu ve küresel bellek durumu üzerinde ne tür işlevler hesaplayabileceğiniz konusunda oldukça ciddi kısıtlamalar getirir.
Her şeyden önce, her Stack Cats programının kendi kendini simetrik olması gerekir. Yukarıdaki kaynak kod için böyle olmadığını fark edebilirsiniz. Bu nedir -l
bayrak içindir: zımnen merkezi için ilk karakteri kullanılarak sola kodunu yansıtır. Dolayısıyla gerçek program:
[<(*>=*(:)*[(>*{[[>[:<[>>_(_-<<(-!>)>(>-)):]<^:>!->}<*)*[^:<)*(>:^]*(*>{<-!<:^>[:((-<)<(<!-)>>-_)_<<]>:]<]]}*<)]*(:)*=<*)>]
Tüm kodla etkin bir şekilde programlama yapmak önemsiz ve sezgisel değildir ve henüz bir insanın nasıl yapabileceğini henüz çözemedi. Daha basit işler için bu tür bir programı zorla uyguladık, ancak buna yakın bir yere elle gidemezdik. Neyse ki, programın bir yarısını görmezden gelmenizi sağlayan temel bir kalıp bulduk. Bu kesinlikle yetersiz kalsa da, şu anda Stack Cats'te etkin bir şekilde programlamanın bilinen tek yolu.
Yani bu cevapta, söz konusu modelin şablonu şudur (nasıl yürütüldüğü konusunda bazı değişkenlikler vardır):
[<(...)*(...)>]
Program başladığında, yığın bandı şöyle görünür 4
:
4
... -1 ...
0
^
[
Hamle sola yığının üst (ve birlikte teyp kafası) - bu "itme" diyoruz. Ve <
bant başını rahatça hareket ettirir. Öyleyse ilk iki komuttan sonra, şu duruma sahibiz:
... 4 -1 ...
0 0 0
^
Şimdi, (...)
koşul olarak kolayca kullanılabilecek bir döngü var: döngü yalnızca geçerli yığının tepesi pozitif olduğunda girilir ve bırakılır. Şu anda sıfır olduğundan, programın ilk yarısının tamamını atlarız. Şimdi merkez komutu *
. Bu basitçe XOR 1
bu yığının en en az bit geçiş yapar ve bu durumda döner, yani 0
bir içine 1
:
... 1 4 -1 ...
0 0 0
^
Şimdi bunun ayna görüntüsüyle karşılaşıyoruz (...)
. Bu kez yığınının üst pozitif ve biz yapmak kodunu girin. Parantez içinde neler olup bittiğine bakmadan önce, sonunda nasıl sarılacağımızı açıklamama izin verin: Bu bloğun sonunda, bant kafasının tekrar pozitif bir değerde olmasını sağlamak istiyoruz (böylece döngü sağa yığın çıkışı tutar ve yığın bu doğru,) tek bir yineleme sonra sona erer ve bir doğrusal koşullu olarak sadece kullanılan bu bir tutar -1
. Eğer durum buysa, döngüyü terk >
eder, çıktı değerine doğru hareket eder ve ]
onu yukarı doğru iter, -1
böylece çıktı için temiz bir yığına sahip oluruz.
Bu budur. Şimdi parantez içinde, bir önceki paragrafta anlatılanları ayarladıktan sonra (bazı itme ve bant kafa hareketleriyle kolayca yapılabilir) yaptığımızdan emin olarak, ilkelliği kontrol etmek istediğimiz her şeyi yapabiliriz. İlk önce Wilson teoremiyle problemi çözmeye çalıştım ancak 100 bayttan fazla bitti, çünkü kare faktörlü hesaplama aslında Stack Cats'de oldukça pahalı (en azından kısa bir yol bulamadım). Bu yüzden deneme bölümüne gittim ve bu gerçekten çok daha kolaylaştı. İlk lineer bit'e bakalım:
>:^]
Bu komutlardan ikisini zaten gördünüz. Ek olarak, :
mevcut yığının ilk iki değerini değiştirir ve ^
XOR, ikinci değeri üst değere değiştirir. Bu, :^
boş bir yığındaki bir değeri çoğaltmak için ortak bir kalıp oluşturur (değerin üstüne bir sıfır çekeriz ve sonra sıfıra çeviririz 0 XOR x = x
). Bundan sonra, bölüm kasetimiz şöyle görünür:
4
... 1 4 -1 ...
0 0 0
^
Uyguladığım deneme bölümü algoritması girdi için çalışmıyor 1
, bu durumda kodu atlamalıyız. Biz kolayca haritalayabilirsiniz 1
için 0
ve pozitif değerlere her şey *
, işte biz bunu nasıl:
*(*...)
Yani, biz çevirmek olduğunu 1
içine 0
, biz gerçekten alırsanız kodun büyük bir kısmını atlamak 0
, ama biz hemen geri içeri *
geri bizim giriş değeri elde böylece. Parantezin sonunda pozitif bir değere sahip olduğumuzdan ve tekrar başlamadan emin olmaları gerektiğinden emin olmalıyız. Koşullu içinde bir yığını sağa çekip >
ana deneme bölümü döngüsünü başlatırız:
{<-!<:^>[:((-<)<(<!-)>>-_)_<<]>:]<]]}
Parantezler (parantezlerin tersine) farklı bir döngü türü tanımlar: bu bir süre bitiminde döngü anlamına gelir, bu her zaman en az bir yineleme için çalıştığı anlamına gelir. Diğer fark, sonlandırma koşulu: Yığın Cat'e girerken mevcut yığının en üst değerini hatırlar ( 0
bizim durumumuzda). Döngü daha sonra bu aynı değer tekrarlamanın sonunda tekrar görülene kadar çalışacaktır. Bu bizim için uygundur: her yinelemede bir sonraki potansiyel bölenin kalanını hesaplıyoruz ve döngüye başladığımız bu yığının üzerine taşıyoruz. Bir bölen bulduktan sonra, kalan 0
ve döngü durur. Böylelikle başlayan bölenleri dener n-1
ve sonra düşürürüz 1
. Bunun anlamı: a) ulaştığımızda sona ereceğini biliyoruz.1
en geç ve b) daha sonra denediğimiz son böleni inceleyerek sayının asal olup olmadığını belirleyebiliriz (eğer öyleyse 1
asal, yoksa değil).
Hadi hadi bakalım. Başlangıçta kısa bir doğrusal bölüm var:
<-!<:^>[:
Şu anların çoğunun ne yaptığını biliyorsun. Yeni komutlar -
ve !
. Yığın Kediler'de artış veya azalma operatörleri yoktur. Bununla birlikte -
(olumsuzlama, yani çarpma -1
) ve !
(bitsel DEĞİL, yani çarpma -1
ve azaltma) vardır. Bunlar bir artım !-
veya azalmayla birleştirilebilir -!
. Bu yüzden n
, üstündeki kopyasını azaltıyoruz -1
, sonra n
yığın üzerinde sola doğru başka bir kopya oluşturuyoruz , sonra da yeni deneme bölenini alıp altına koyuyoruz n
. Böylece ilk tekrarda şunu anlıyoruz:
4
3
... 1 4 -1 ...
0 0 0
^
Diğer yinelemelerde, bir 3
sonraki test böleni ve benzeri ile değiştirilecektir (oysaki iki kopya n
bu noktada her zaman aynı değerde olacaktır).
((-<)<(<!-)>>-_)
Bu modulo hesaplama. Döngüler pozitif değerler üzerinde sonlandırıldığı için, fikir pozitif bir değer elde edene kadar -n
deneme bölenini baştan başlamak ve tekrar tekrar kullanmaktır d
. Bir kez yaptığımızda sonucu çıkarırız d
ve bu bize kalanı verir. Buradaki zor bit -n
, yığının üstüne bir şey koymayıp ekleyen bir döngü başlatmamamızdır d
: yığının tepesi negatifse döngü girilmez. Bunlar tersinir bir programlama dilinin sınırlamalarıdır.
Dolayısıyla bu sorunu aşmak n
için yığının tepesinden başlıyoruz , ancak yalnızca ilk yinelemede reddediyoruz. Yine, bu göründüğünden daha basit geliyor ...
(-<)
Yığının tepesi pozitif olduğunda (yani yalnızca ilk yinelemede), onu yok sayırız -
. Ancak, tam olarak yapamayız (-)
çünkü o zaman iki kez uygulanıncaya kadar döngüyü terk etmeyecektik -
. Böylece bir hücreyi sola doğru hareket ettiriyoruz <
çünkü orada pozitif bir değer olduğunu biliyoruz 1
. Tamam, şimdi n
ilk yinelemeyi güvenilir bir şekilde ihmal ettik . Ancak yeni bir sorunumuz var: Teyp kafası şimdi ilk yinelemede diğerinden farklı bir konumda. Devam etmeden önce bunu pekiştirmemiz gerekiyor. Daha <
sonra şerit kafasını sola hareket ettirir. İlk yinelemede durum:
-4
3
... 1 4 -1 ...
0 0 0 0
^
Ve ikinci yinelemede (şimdiye bir d
kere eklediğimizi unutmayın -n
):
-1
3
... 1 4 -1 ...
0 0 0
^
Bir sonraki koşullu bu yolları tekrar birleştirir:
(<!-)
İlk yinelemede, teyp kafası sıfıra işaret eder, bu yüzden bu tamamen atlanır. Diğer yinelemelerde, bant başı bir tane gösteriyor, bu yüzden bunu yapıyoruz, sola doğru hareket ediyoruz ve oradaki hücreyi arttırıyoruz. Hücrenin sıfırdan başladığını bildiğimiz için, her zaman pozitif olacaktır, böylece döngüyü terk edebiliriz. Bu, her zaman ana yığının solundaki iki yığının sonuna kadar kalmamızı ve şimdi geri hareket etmemizi sağlar >>
. Sonra modulo döngüsünün sonunda yaparız -_
. Sen zaten biliyorsun -
. _
XOR'nin ne ^
olduğunu çıkarmaktır : yığının tepesi a
ve altındaki değer ise ile b
değiştirilir . Biz ilk etkisiz yana olsa da, yerini ile suretle ekleyerek,a
b-a
a
-_
a
b+a
d
çalışan toplamımızın içine.
Döngü sona erdikten sonra (bir pozitif değere ulaştık), teyp şöyle görünür:
2
3
... 1 1 4 -1 ...
0 0 0 0
^
En soldaki değer herhangi bir pozitif sayı olabilir. Aslında, eksi bir yineleme sayısı. Şimdi başka bir kısa doğrusal bit var:
_<<]>:]<]]
Daha önce de söylediğim gibi d
, gerçek kalanı ( 3-2 = 1 = 4 % 3
) elde etmek için sonucu çıkarmamız gerekir , bu yüzden sadece bir _
kez daha yaparız . Daha sonra, solda arttırdığımız yığını temizlememiz gerekiyor: bir sonraki böleni denediğimizde, ilk yinelemenin çalışması için tekrar sıfır olması gerekiyor. Böylece oraya taşınıyoruz ve bu pozitif değeri diğer yardımcı istifin üzerine itiyoruz <<]
ve sonra diğer istif aracımızla birlikte diğer istif aracımıza geçiyoruz >
. Biz yukarı çekin d
ile :
ve üzerine geri itin -1
ile ]
ve sonra biz bizim koşullu yığının üzerine kalan taşımak <]]
. Bu, deneme bölümü döngüsünün sonu: bu, sıfır kalanını alana kadar devam eder, bu durumda sola yığınn
en büyük bölen (hariç n
).
Döngü sona erdikten sonra, *<
giriş ile 1
tekrar yolları birleştirmeden hemen önce var . *
Basitçe içine sıfır döner 1
biz biraz da gerekir, ve sonra biz bölen taşımak <
(biz girişi için aynı yığın üzerinde yapmayacak şekilde 1
).
Bu noktada, üç farklı girdi türünün karşılaştırılmasına yardımcı olur. İlk olarak, n = 1
bu deneme bölümü işlerinden hiçbirini yapmadığımız özel durum :
0
... 1 1 -1 ...
0 0 0
^
O zaman, önceki örneğimizde n = 4
bileşik sayı:
2
1 2 1
... 1 4 -1 1 ...
0 0 0 0
^
Ve son olarak n = 3
, bir asal sayı:
3
1 1 1
... 1 3 -1 1 ...
0 0 0 0
^
Yani asal sayılar için, 1
bu yığında bir tane var ve birleşik sayılar için, ya 0
daha büyük olan bir ya da artı sayımız var 2
. Bu durumu 0
ya 1
da aşağıdaki son kod parçasına ihtiyacımız var:
]*(:)*=<*
]
sadece bu değeri sağa iter. Daha sonra *
koşullu durumu büyük ölçüde basitleştirmek için kullanılır: en az anlamlı birayı değiştirerek, 1
(prime) 0
, 0
(kompozit) pozitif değere çeviririz 1
ve diğer tüm pozitif değerler hala pozitif kalır. Şimdi sadece 0
pozitif ile pozitif arasında bir ayrım yapmamız gerekiyor . Başka kullandığımız yer orası (:)
. Yığının üstü ise 0
(ve girdi bir asal ise), bu basitçe atlanır. Ancak, yığının tepesi pozitifse (ve girdi birleşik sayıysa) bu 1
, onu şu şekilde değiştirir 0
;1
Asal sayılar için - sadece iki farklı değer. Tabii ki, bizim çıktı almak istediklerimizin tam tersi, ancak bu kolaylıkla başka biriyle sabitlenebilir *
.
Şimdi geriye sadece tüm çevreleyen çerçeve tarafından beklenen yığınlar desenini restore etmektir: sağa yığının üstüne, olumlu değerine teyp kafası neden ve tek bir -1
yığın sağda olduğunu . Bunun =<*
için var. =
iki bitişik yığının üstünü değiştirerek -1
sonucun sağına hareket eder , örneğin 4
tekrar giriş yapmak için :
2 0
1 3
... 1 4 1 -1 ...
0 0 0 0 0
^
Sonra sola hareket ediyoruz <
ve bu sıfırı olana çeviriyoruz *
. Ve bu o.
Programın nasıl çalıştığını daha derine kazmak istiyorsanız, hata ayıklama seçeneklerinden yararlanabilirsiniz. -d
Bayrağı ekleyin ve "
mevcut hafıza durumunu görmek istediğiniz yere yazın, örneğin bu şekilde yapın veya tüm programı tam olarak izlemek için -D
bayrağı kullanın . Alternatif olarak, adım adım hata ayıklayıcılı bir Stack Cats yorumlayıcısı içeren Timwi'nin Ezoterik Kullanımı'nı kullanabilirsiniz .