Mar 22 2009

ตัวอย่างโค้ด C# เปรียบเทียบกับโค้ดภาษาเชิง functional อื่นๆ

Category: .NETm3rLinEz @ 20:01

วันก่อนอ่านเจอกระทู้คล้ายๆ Code Golf ที่ Stackoverflow จาก delicious ของ Jemmy ครับ เป็นกระทู้ในเว็บ Narisa.com

ลองพยายามแปลงโค้ดจากภาษา Scala ของพี่ข้าวโพดหวานให้เป็น C# ดู เพราะพอรู้มาว่า C# 3.5 มันทำอะไรแปลกๆได้มากขึ้น ได้ผลเปรียบเทียบดังนี้ครับ

แก้ไข: เพิ่งไปอ่านมาว่า Scala มันเป็นภาษาแบบ multi-paradigm ไปบอกว่าของเค้าเป็น functional อย่างเดียวเด๋วโดนว่า ฮ่าๆ

อันนี้เป็น Scala

def findPrimeFactors(number : Int) : List[Int] = {
val first_prime_or_none = (2 until Math.sqrt(number)).inclusive.find { i => number % i == 0 }
if (first_prime_or_none != None) {
return first_prime_or_none.get :: findPrimeFactors(number / first_prime_or_none.get)
}
number :: Nil
}

(2 until 100).inclusive.foreach {
number => println( "Prime factors of " + number + " is " +
findPrimeFactors(number).removeDuplicates.mkString("[", ", ", "]") )
}

อันนี้เป็น C#

static void Main(string[] args)
{
for (int number = 2; number <= 33; number++)
Console.WriteLine("Prime factors of {0} is [{1}]",
number, String.Join(", ", FindPrimeFactors(number)
.ToList().ConvertAll<string>(x => x.ToString())
.ToArray())
);
}

static HashSet<int> FindPrimeFactors(int number)
{
var firstPrimeOrNone = Enumerable.Range(2, (int)Math.Ceiling(Math.Sqrt(number)))
.ToList().Find(x => number % x == 0);

if (firstPrimeOrNone != 0)
{
var ret = FindPrimeFactors(number / firstPrimeOrNone);
ret.Add(firstPrimeOrNone);
return ret;
}

return new HashSet<int>(new int[] { number }); ;
}

C# เวอร์ชันลอกการบ้านชาคริตมาแก้นิดแก้หน่อย

อันนี้ลองใช้ IEnumerable กับ yield ทำ state machine ง่ายๆครับ อ่านง่ายกว่าอันข้างบนแยะ

static void Main(string[] args)
{
for (int number = 2; number <= 33; number++)
Console.WriteLine("Prime factors of {0} is [{1}]",
number, String.Join(", ", FindPrimeFactors(number)
.Distinct().Select(x => x.ToString()).ToArray())
);
}

static IEnumerable<int> FindPrimeFactors(int number)
{
while (true)
{
var firstPrimeOrNone = Enumerable.Range(2, (int)Math.Ceiling(Math.Sqrt(number)))
.ToList().Find(x => number % x == 0);

if (firstPrimeOrNone != 0)
{
yield return firstPrimeOrNone; number /= firstPrimeOrNone;
}
else
{
yield return number; break;
}
}
}


Tags: ,

Comments

1.
chakrit chakrit says:

ไม่ต้องแปลงกลับไปกลับมาขนาดนั้นก็ได้นะ ใช้ IEnumerable ให้เต็มที่เลยดีกว่า

    static void Main(string[] args)
    {
        foreach (var number in Enumerable.Range(2, 32)) {
            Console.WriteLine("Prime factors of {0} is [{1}]",
                number, string.Join(", ", FindPrimeFactors(number)
                    .Distinct()
                    .Select(n => n.ToString())
                    .ToArray()));
        }
    }

    static IEnumerable<int> FindPrimeFactors(int number)
    {
        var firstPrime = Enumerable
            .Range(2, (int)Math.Ceiling(Math.Sqrt(number)))
            .Where(x => number % x == 0)
            .FirstOrDefault();

        if (firstPrime != 0) {
            foreach (var i in FindPrimeFactors(number / firstPrime))
                yield return i;

            yield return firstPrime;

        } else
            yield return number;
    }

อิอิ มาเขียน C# เล่นตอนตีสี่ T.T ไม่ีมีแรงไปทำงานแน่เลย

ว่าแต่ว่าทำไมอัลกอริทึมนี้ถึงได้เลข prime อย่างเดียวอ่า?!? แอบอยากรู้ math เหมือนเคยเรียนแต่ลืมไปนานแล้ว เหอๆ

2.
m3rlinez m3rlinez says:

โอ้สสส เคยเห็น IEnumerable กับ yield แล้ว แต่ไม่เคยคิดได้ว่ะว่าจะใช้ตอนไหน

ดูโค้ดแกแล้วได้อะไรเพิ่มหลายอย่างว่ะ .Distinct .Where กับ .Select ยังไม่เคยใช้เลย เด๋วลองๆ

ส่วน Math มันคือ ดึงตัวประกอบที่น้อยที่สุดออกมาเรื่อยๆ

แน่นอนว่าตัวประกอบน้อยสุดที่ดึงออกมาแต่ละรอบต้องเป็น Prime ชัวร์ (วนจากน้อยไปมาก)

เพราะถ้าดึงออกมาแล้วไม่เป็น Prime แปลว่าต้องมีตัวประกอบที่น้อยกว่ามันอยู่แน่ๆ ซึ่งควรจะถูกดึงออกมาก่อน => Contradiction

Comments are closed