(Aşağıdakileri senin yanıtını görmeden yazmışım.)
Alıntı (erdem):
> Girişten sayıları okuyan bu sınıfın D sürümünü yapmaya çalışıyorum.
http://introcs.cs.princeton.edu/java/stdlib/StdIn.java.html
Önce hatırlıyorum: Standart giriş bir karakter akımıdır. (Senin bunları bildiğinden neredeyse eminim ama yine de duramadım ve yazdım. :))
O sınıf asıl işlerini java.util.Scanner'a yaptırıyor. Scanner (galiba) düzenli ifadelerden anlayan bir akım ayrıştırıcıymış.
Phobos'ta bu kavramlara yakın olarak std.format ve bir de emekliye ayrılacak olan std.cstream var. (std.format.formattedRead'i aşağıda kullandım.) Bunlara ek olarak, aralık kavramını destekleyen bir çatı düşünülüyor ama temeli karakterlere (ve hatta UTF-8 kodlarına) dayandığı için bunun tam olarak çözülebileceğini sanmıyorum.
Örneğin, int okunmasını istediğimizde girişte "ab" varsa hata kabul edilir ama "1abc" varsa 1 int olarak okunur.
Akımlarda okuma ve yazma birbirinin karşıtı da olamaz. İki int'i 10 ve 20 olarak yazsak okurken 1020 diye tek int olarak okumak zorundayızdır.
Bu tür garipliklerin bir örneği, gösterdiğin sınıfın da içine düştüğü "true", "1", "false", ve "0" dizgilerinin bool değerler olarak kabul edilmeleridir. Ya o 1 ve 0 yanlışlık sonucu araya girmiş int iseler? Yani, program içinde sıfır olmayan değerler true anlamına geliyorlar ama dosyaya 42 diye yazılmış iseler true diye okunamıyorlar. Çelişkili.
Umarım bunlar karakter akımlarının güçlüklerini göstermeye yetiyordur.
Alıntı:
> Bunu kullanan örnek şu şekilde:
> while (!StdIn.isEmpty()) {
> int p = StdIn.readInt();
> int q = StdIn.readInt();
> /* ... */
> }
> ```
>
> Şimdi burada dikkat ederseniz, giriş boş olmadığı sürece girişten okuyor. Aynı zamanda readInt()'de sadece bir tane tamsayı döndürüyor.
Programların standart girişlerine bağlı olan karakter akımlarının kaynağının durumu program tarafından bilinemez. Örneğin "daha 5 karakter" kaldı gibi bir bilgiyi program edinemez. Bunun nedeni, standart girişin (çoğu durumda olduğu gibi) konsola da bağlı olabileceğidir. Konsolda oturan kişinin de girişi hemen mi sonlandıracağı yoksa daha on sayfa yazdıktan sonra mı kapatacağı bilinemez.
Alıntı:
>
> Programı kullanırken de java programadı < giriş.txt şeklinde girişten verileri okuyor.
>
O durum biraz daha iyi gibi görünebilir çünkü giriş bir dosyaya bağlı. Ama yine de yeterli değildir çünkü program yine de bir karakter akımı olarak görmek zorundadır. Programın girişinde "sana 100 karakter veriyoruz" diye bir arayüz yoktur.
Ek olarak, konsol durumundaki klavyenin iç karakter belleği de dahil olmak üzere arada birden fazla ara bellek bulunabilir. Bunlar etkinlik adına bilgiyi programa belirli büyüklükteki parçalar halinde veriyor olabilirler. Örneğin bazı katmanlar ancak satır sonu karakteri gördükçe bilgiyi satır satır aktarırlar.
İşte o yüzden girişin sonuna gelinip gelinmediği ancak ve ancak girişten karakter okumaya çalıştıktan sonra ve o okuma başarısızlıkla sonuçlanmışsa ve o başarısızlık bir hata nedeniyle değil ise anlaşılabilir.
Yeni başlayanların (ve deneyimlilerin de :)) içine düştükleri bir hata:
import std.stdio;
void main()
{
auto dosya = File("deneme_dosyasi");
while (!dosya.eof) {
int i;
dosya.readf(" %s", &i);
writeln("okudum: ", i);
}
}
deneme_dosyasi'nın içinde "1 2 3" satırı bulunduğunu düşünelim. Ama sonda bir tane de boş satır olsun. Nasıl olsa o satırı yazıp sonunda Enter'a basmak ve kaydetmek yaygın bir davranıştır. Öyle yapıldığında program dört int okur (!): 1, 2, 3, ve 0. :p
Doğrusu, readf ile int okumaya *yeltendikten* sonra kaç dönüşümün başarılı olduğuna bakmaktır:
immutable dönüşümAdedi = dosya.readf(" %s", &i);
if (dönüşümAdedi == 1) {
writeln("okudum: ", i);
}
Veya eof tekrar denetlenebilir:
if (!dosya.eof) {
writeln("okudum: ", i);
}
Bütün bunlara bakarak, Scanner'ın girişin boş olup olmadığı kavramı için girişten önceden karakterler toplamış olduğunu ve isEmpty() kavramı için elinde karakter bulunup bulunmadığına baktığını düşünebiliriz.
Alıntı:
>
> Örnek bir giriş dosyası da bu şekilde:
>
> 10
> 4 3
> 3 8
> 6 5
> 9 4
> 2 1
> 8 9
> 5 0
> 7 2
> 6 1
> 1 0
> 6 7
>
> D ile bu şekilde bir Giriş sınıfı yazsak buradaki bilgilere göre bir kuyruk mu kullanmamız gerekir.
Olur ama şart değil. Tabii ki okudukça dizinin sonuna eklenebilir. Dikkat edersen onlar da bu sınıfa readInts() diye bir işlev eklemişler. O işlev girişin *geri kalanını* int'ler olarak okuyor. Ne yazık ki, readInts() de girişten okumanın güçlüğünün başka bir göstergesi: Yani bir dizi int ancak en sonda gelebiliyor. :( (Arada okumaya kalksak kendisinden sonra gelen ve olasılıkla diziye ait olmayan int'leri veya int olabilecek karakterleri de okurdu.)
Alıntı:
> Bir de örneğin bu sınıfta no = new int[N];'in esprisi nedir.
no'yu istersen boş olarak oluşturup okunan int'leri onun sonuna ~= ile de ekleyebilirsin. Öyle yaptığında dizi büyüdükçe yeni yer ayrılması ve elemanların kopyalanmaları gerekecektir. Eğer baştan kaç tane olduğunu bilirsen 'new int[N]' ile hepsi için o kadarlık yer ayırıyorsun. Ama aslında N adetlik gerçek bir dizi oluşuyor. Yani bütün elemanları yasaldır. Eğer bütün amaç bellekte yeni yer(ler) ayrılmasını önlemek ise std.array.reserve ile N adetlik yer de ayrılabilirdi. (Veya geçende de konuştuğumuz gibi, std.array.Appender da kullanılabilirdi.)
Ama şunu da düşünmek gerek: bütün int'leri okuyup bir diziye yerleştirmek şart mı? Yani o int'leri ancak ondan sonra mı kullanacağız? Eğer şart değilse, yani int'ler okundukça işlenebiliyorlarsa bu işi tembel olarak da halledebiliriz. byLine() File'ın içeriğini satır satır gösterir.
Ama dikkat: hızlı olsun diye hep içinde tuttuğu aynı string'i kullanır. Yani 'satır'ı doğrudan bir diziye yerleştiremezsiniz. satır.dup (veya satır.idup) ile kopyasını almak gerekir.
Benim aşağıda satırın kopyasına ihtiyacım yok:
import std.stdio;
import std.format;
import std.array;
void main()
{
foreach (satır; stdin.byLine()) {
while (!satır.empty) {
int p;
int q;
immutable dönüşümAdedi = formattedRead(satır, " %s %s", &p, &q);
if (dönüşümAdedi != 2) {
writefln("Çift int bulamadım. :(");
} else {
writefln("p: %s, q: %s", p, q);
}
}
}
}
Son olarak, bu Java sınıfının bir benzerini ddio.d ismiyle yazmaya başlamıştık. Forumda aratabilirsin. O sınıfın temeli şuradaki sayıOku() işlevine dayanıyor:
http://www.ddili.org/ders/d/islevler.html
Ali
--
[ Bu gönderi, <http://ddili.org/forum>'dan dönüştürülmüştür. ]