Geçen gün İngilizce forumdaki bir soruya yanıt yazdım. Sonuçtaki program (henüz son kullanıma hazır olmasa da) D'nin ne kadar güçlü olduğunu gösterdi:
http://forum.dlang.org/thread/oscliyovyysbxmdhyafe@forum.dlang.org
Phobos'ta bir düzenli ifade modülü var: std.regex. Hatta, bu modülün ctRegex olanağı, düzenli ifadeyi ayrıştırma düzeneğini derleme zamanında oluşturan ve bu yüzden çalışma zamanında hız kazandıran bir olanaktır.
Düzenli ifadelerin bir sorunu, ayrıştırma işleminin tarifinin çok karmaşık olabilmesidir. Örneğin, yukarıdaki sorudaki ayrıştırma işi şuydu: "Önce parantezler arasında int değer gelecek, sonra tek char gelecek, sonra da yine parantezler arasında bir double değer gelecek." Amaç, verilen bir dizgiyi buna göre ayrıştırmak ve o tarife uyan yerlerdeki int, char, ve double değerleri bir çokuzlu olarak döndürmek. Bunu yapan bir düzenli ifade dizgisi aşağıdaki gibidir:
`[(]([0-9]+)[)](.)[(]([-+]?[0-9]*\.?[0-9]+([eE][-+]?[0-9]+)?)[)]`
Soruyu soran kişi onun yerine şöyle bir tarif olabilir mi diye sormuştu:
decode!("(", int, ")", char, "(", double, ")")
Dikkat ederseniz yukarıda Türkçe ifade olarak yazdığımın aynısı ama şablon parametreleri halinde veriliyor.
Yukarıdaki bağlantıdaki programı hem biraz düzelttim hem Türkçeleştirdim:
import std.stdio;
import std.string;
import std.regex;
import std.typecons;
import std.conv;
import std.algorithm;
import std.range;
template regexSınıflandırması(T) {
static if (is (T == int)) {
// Uyarı: "012"yi ondalık olarak alır ve değeri 12 olur. Yani, değeri
// 10 olan sekizli olarak değil.
enum regexSınıflandırması = `[0-9]+`;
} else static if (is (T == char)) {
enum regexSınıflandırması = `.`;
} else static if (is (T == double)) {
enum regexSınıflandırması = `[-+]?[0-9]*\.?[0-9]+([eE][-+]?[0-9]+)?`;
} else {
static assert(false,
format("'%s' türü henüz desteklenmiyor.", T.stringof));
}
}
string regexEscape(string s) {
// TODO: Bu diziye başka karakterler de eklemek ve işlevin mantığını
// düzeltmek gerekiyor.
enum özelRegekKarakterleri = [ '(', ')' ];
return s.map!(c => (özelRegekKarakterleri.canFind(c)
? format("[%s]", c)
: format("%s", c)))
.joiner
.text;
}
auto ayrıştırmaParametreleriniAyrıştır(Args...)(string uyanElemanİsmi) {
string regexDizgisi;
string çokuzluDöndürmeİfadesi = "return tuple(";
size_t uyanınNumarası = 1;
foreach (arg; Args) {
static if (is (arg)) {
regexDizgisi ~= format("(%s)", regexSınıflandırması!arg);
çokuzluDöndürmeİfadesi ~=
format("%s[%s].to!%s, ",
uyanElemanİsmi, uyanınNumarası, arg.stringof);
++uyanınNumarası;
} else static if (is (typeof(arg) == string)) {
regexDizgisi ~= regexEscape(arg);
} else {
static assert(false, format("'%s' ifadesi geçersiz.", arg));
}
}
çokuzluDöndürmeİfadesi ~= ");";
return tuple(regexDizgisi, çokuzluDöndürmeİfadesi);
}
auto ayrıştır(Args...)(string s) {
enum ayrıştırmaDizgileri = ayrıştırmaParametreleriniAyrıştır!Args("e");
enum regexDizgisi = ayrıştırmaDizgileri[0];
enum çokuzluDöndürmeİfadesi = ayrıştırmaDizgileri[1];
enum r = ctRegex!regexDizgisi;
// Ne olup bittiğini gözlemlemek için kullanılabilir:
// pragma(msg, regexDizgisi);
// pragma(msg, çokuzluDöndürmeİfadesi);
auto uyum = s.match(r);
if (uyum) {
// Aynı satırda birden fazla uyum olduğunda birden fazla eleman
// olur. Biz burada ilk elemanı kullanıyoruz ve gerisini gözardı
// ediyoruz. (Tabii o yüzden foreach'e de gerek yok ama genel
// kullanımı göstermesi açısından çıkartmadım.)
foreach (e; uyum) {
mixin (çokuzluDöndürmeİfadesi);
}
}
// D'nin başka bir kolaylığı: typeof(return), "bu işlevin dönüş türü"
// anlamına gelir. O tür yukarıda mixin ile katılan çokuzluDöndürmeİfadesi
// içinde gizli olduğundan burada haberimiz bile olmayabiliyor. O yüzden
// burada bu kolaylıktan yararlanıyoruz.
return typeof(return)();
}
void main() {
auto t = ayrıştır!("(", int, ")", char, "(", double, ")")("(1)-(2.5)");
writeln(t);
// Birden fazla kere kullanılacak olan bir ayrıştırıcı oluşturuyoruz.
auto ayrıştırıcı = (string s) => ayrıştır!(int, "/", double)(s);
// Bütün elemanlara aynı ayrıştırıcıyı uyguluyoruz.
auto ayrıştırılanlar = ["1/1.5", "2/2.5", "3/3.5"]
.map!ayrıştırıcı;
writeln(ayrıştırılanlar);
}
Ali
--
[ Bu gönderi, http://ddili.org/forum'dan dönüştürülmüştür. ]
Permalink
Reply