Thread overview
Yapı dizisi içinde sıralama
Feb 26, 2018
zafer
Feb 26, 2018
kerdemdemir
Feb 26, 2018
kerdemdemir
Mar 03, 2018
zafer
February 26, 2018

Merhaba,

Elimde kendi geliştirdiğim bir yapı dizisi var. Benim yapmak istedğim yapı elemanlarının tarih değerlerine bakarak bir dizi içinde bulunan bu yapıları sıralamak. Bunun için aşağıdaki gibi bir test kodu geliştirdim ve işimi görüyor. Ancak bunun daha güzel bir yolu varmı diye sormak istiyorum. Ayrıca opCmp() kullanmadan .sort() metodunu işletebilir miyim?

Bir diğer sorum .sort() metodu bir SortedRange!() aralığı döndürüyor. Bunu başka bir metoda nasıl parametre olarak gönderebilirim?

import std.stdio: writefln;
import std.datetime: DateTime;
import std.algorithm.sorting;

struct Kitap
{
	int no;
	DateTime tarih;
	string isim;

	int opCmp(Kitap k) const
	{
		int sonuc = 0;
		if (tarih < k.tarih) sonuc = -1;
		if (tarih > k.tarih) sonuc = 1;
       return sonuc;
   }
}

void main()
{
	Kitap k3 = Kitap(3, DateTime(2018, 2, 22, 00, 00, 00), "Phyton 3 Veri Yapıları");
	Kitap k1 = Kitap(1, DateTime(2018, 2, 14, 00, 00, 00), "D Programlama Dili");
	Kitap k2 = Kitap(2, DateTime(2018, 2, 18, 00, 00, 00), "Her Yönüyle Python");

	Kitap[] kitaplar = [k1, k2, k3];

	listele(kitaplar);
}

void listele(Kitap[] kitaplar)
{
	writefln("Sirali Kitap Listesi :");
	foreach (kitap; kitaplar.sort!((a, b) => a < b))
	{
		writefln("--> %d. %s", kitap.no, kitap.isim);
	}
}

--
[ Bu gönderi, http://ddili.org/forum'dan dönüştürülmüştür. ]

February 26, 2018

Selam Zafer,

Ben opCmp yazmaya üşenir şöyle yapardım :

void listele(Kitap[] kitaplar)
{
writefln("Sirali Kitap Listesi :");
foreach (kitap; kitaplar.sort!((a, b) => a.tarih < b.tarih))
{
writefln("--> %d. %s", kitap.no, kitap.isim);
}
}

Ama seninki daha güzel.

Şu sortedRange türünü kopyalama yapmadan nasıl parametre olarak geçeriz bilmiyorum ben. Belki Ali Abi biliyordur. Ama ben acımayıp .array methodunu kullanıyorum hep bu durumlarda.

import std.array;// Bunu unutmayasın

void KitapListesiYaz( Kitap[] range )
{
writeln(range);
}

void listele(Kitap[] kitaplar)
{
writefln("Sirali Kitap Listesi :");
auto range = kitaplar.sort!((a, b) => a.tarih < b.tarih);
KitapListesiYaz(range.array);
}

Erdemdem

--
[ Bu gönderi, http://ddili.org/forum'dan dönüştürülmüştür. ]

February 26, 2018

Vay arkadaş,

Ben kopyalama diye çekiniyordum halbuki logN'den N karmaşıklığına düşmek daha çok etkiliyormuş hiç fikrim yoktu.

Bir çok bilgi içeren çok güzel bir cevap oldu benim acımdan.

Erdemdem

--
[ Bu gönderi, http://ddili.org/forum'dan dönüştürülmüştür. ]

February 26, 2018

Evet, opCmp o tür için şüphe götürmez bir sıralama kararı bulunduğunda mantıklı oluyor. Örneğin bir kesirli sayı türünün değerine göre sıralanması mantıklı. Dediğiniz gibi, Kitap gibi bir türde ise sıralamanın nasıl olacağı duruma göre değişir. Örneğin, bazen yazar ismine göre sıralamak gerekir.

sort() diziyi zaten sıralıyor. O yüzden .array çağırmadan doğrudan kullanılabilir.

SortedRange dönüyor olmasının yararı, SortedRange'in üye işlevlerinin bu durumdan yararlanıp çok daha hızlı olabilen ikili arama algoritmasını kullanıyor olmaları. (SortedRange yalnızca RandomAccessRange'lerle kullanılabiliyor.) SortedRange'den yararlanan işlevlerden en doğal olanı std.algorithm.find'dır: Gelen aralık bir SortedRange olduğunda O(log(N)) karmaşıklığındaki SortedRange.equalRange'i çağırır:

import std.stdio;
import std.range;
import std.algorithm;
import std.datetime.stopwatch;
import std.string;

enum elemanAdedi = 1_000_000;
enum ölçümAdedi = 1_000;
const static int arananSayı;

static this() {
   // Sırayla aramada ortalama süre tutsun diye ortalarda bir değer arayalım

   import std.random;
   enum hataPayı = 1000;
   static assert(elemanAdedi > hataPayı);
   const ek = uniform(-(hataPayı / 2), (hataPayı / 2));
   arananSayı = (elemanAdedi / 2) + ek;

   writefln("Aranan sayı: %s", arananSayı);
}

auto std_algorithm_find_ile(R)(R sayılar) {
   // std.algorithm.find aralık hakkında bir varsayımda bulunamayacağından olan
   // sırayla aramak zorundadır. Karmaşıklık (complexity) genelde O(N)'dir.
   // Ancak, R'nin bir SortedRange olduğunu bildiğinde o da s.equalRange'i
   // çağırır.
   auto sonuç = find(sayılar, arananSayı);
   assert(!sonuç.empty);
   return sonuç.front;
}

auto SortedRange_equalRange_ile(R)(R sayılar) {
   // SortedRange sıralı olduğunu bildiğinden ikili aramayı
   // uygular. Karmaşıklık: O(log(N))
   auto sonuç = sayılar.equalRange(arananSayı);
   assert(!sonuç.empty && sonuç.front == arananSayı);
   return sonuç.front;
}

void main() {
   auto dizi = iota(elemanAdedi).array;
   auto s = sort(dizi);

   const ölçümler = benchmark!(() => std_algorithm_find_ile(dizi),
                               () => std_algorithm_find_ile(s),
                               () => SortedRange_equalRange_ile(s))
                    (ölçümAdedi);

   writefln("std.algorithm.find ile dizi       : %s", ölçümler[0]);
   writefln("std.algorithm.find ile SortedRange: %s", ölçümler[1]);
   writefln("SortedRange.equalRange ile        : %s", ölçümler[2]);
}

SortedRange yöntemi du deneyde binlerce kat hızlı çıkıyor:
'
Aranan sayı: 500061
std.algorithm.find ile dizi : 936 ms, 530 μs, and 2 hnsecs
std.algorithm.find ile SortedRange: 123 μs and 9 hnsecs
SortedRange.equalRange ile : 129 μs and 5 hnsecs
'
Çok önemli olarak, find'ın yavaş kaldığı durumda dizinin kendisini gönderdiğimize dikkat edin. Sıralanmış olan bir aralık üzerinde arama yapmak gerektiğinde SortedRange'i kullanmak şart.

SortedRange.equalRange bir aralık döndürür. Boş olmadığından emin olup .front'unu kullanmak gerekiyor. equalRange'in yararı, birden fazla sayı bulunduğunda hepsine eriştiriyor olması. (Aşağıdaki örnekte birden fazla sayı kullanacağım.)

Aslında bu örnekte bir enayilik var: iota() ile üretilen sayılar yaşamlarına sıralı olarak başlayacaklarından sort()'u çağırmak gereksiz olmalı. Böyle durumlar için "sıralı olduğunu varsay" anlamında assumeSorted var. Bize güvenip SortedRange'i döndürür:

   //    auto s = sort(dizi);
   auto s = dizi.assumeSorted;

Böylece sırama işleminden bile kurtulmuş oluruz.

equalRange'in kullanımını gösteren bir örnek:

import std.stdio;
import std.range;
import std.random;
import std.algorithm;

auto diziOluştur() {
   // Basit olsun diye "emirli" (imperative) yöntem kullanıyorum
   int[] sonuç;
   foreach (_; 0 .. 30) {
       sonuç ~= uniform(0, 10);
   }
   return sonuç;
}

void main() {
   auto dizi = diziOluştur();
   auto s = sort(dizi);
   enum arananSayı = 7;
   writeln("Dizi: %s", s);
   writefln("Dizideki %s değerli bütün elemanlar:", arananSayı);
   foreach (eleman; s.equalRange(arananSayı)) {
       writeln(eleman);
   }
}

Verilen aralığın SortedRange olduğundan bundan yararlanmak isteyen işlev yazmak istediğimizde std.traits'ten yararlanabiliriz:

import std.stdio;
import std.range;
import std.algorithm;

void foo(R)(R aralık) {
   import std.traits;
   static if (isInstanceOf!(SortedRange, R)) {
       writeln("Güzel, SortedRange");

   } else {
       writeln("SortedRange değil");
   }
}

void main() {
   auto dizi = [ 3, 1, 2 ];
   auto s = sort(dizi);
   foo(s);
   foo(dizi);
}

Ali

--
[ Bu gönderi, http://ddili.org/forum'dan dönüştürülmüştür. ]

March 03, 2018

Ali teşekkürler, birçok bilgi içeren güzel bir örnek olmuş özellike find, canFind, benchmark vs. gibi güzel olanaklarıda bu sayade tanımış oldum.

Aralıkları fonksiyonlara geçirmek için şöyle bir yazım kullamışsın;

auto std_algorithm_find_ile(R)(R sayılar) { ...

Bu şekilde tüm aralık türlerini fonksiyonlara geçirebilir miyiz? Yoksa bu kullanım sadece bu örnek için midir?

--
[ Bu gönderi, http://ddili.org/forum'dan dönüştürülmüştür. ]

March 03, 2018

Bir dizi elemanla işlem yapıldığında parametre türünü dilim seçmek gereksiz bir kısıtlama getiriyor. "Mutlaka dizi gerekmiyorsa neden yalnızca dizi üzerinde işleyen işlev olsun" diye düşünülüyor. (Elemanlara rasgele erişim gerektiğinde tabii ki dizi düşünülebilir ama rasgele erişim gerçek hayatta çok nadir gerekiyor. Çoğu döngü foreach ile yazılabilir ve dolayısıyla her InputRange ile kullanılabilir.)

Benim öyle seçmemin asıl nedeni ise find vs. işlevlerin dönüş türlerinin pek telaffuz edilemiyor olması. :) R gibi şablon parametresi kullanınca "tam olarak her ne aralık türü ise" demiş oluyoruz ve o işlev içinde kullanılabilen her türü kabul etmiş oluyoruz.

Ali

--
[ Bu gönderi, http://ddili.org/forum'dan dönüştürülmüştür. ]