‏إظهار الرسائل ذات التسميات لغة البرمجة Scala. إظهار كافة الرسائل
‏إظهار الرسائل ذات التسميات لغة البرمجة Scala. إظهار كافة الرسائل

يناير 05، 2010

نظرة على QtJambi من خلال Scala، JRuby و Jython

تدوينة اليوم ستكون طويلة و الصراحة لا أدري من أين سأبدئها، و عموما  سأتطرق فيها إلى البرمجة. حسننا اعتقد أنني سأبدأ بالحديث عن منصة جنو/لينوكس كأفضل منصة برمجية.

في الفترة الاخيرة إستخدمت منصة ويندوز Windows و منصة Mac OS X بشكل مطول للقيام ببعض الأعمال البرمجية، و تلك كانت المناسبة للنظر إليهما من جديد من منظور مبرمج. لن أدعي أن هتين المنصتين لا يصلحن لشيء و أنهما من شركتين احتكاريتين ووو (كما يتصور بعض المتعصبون)، بل بالعكس الأدوات المتاحة على هتين المنصتين جذابة و متميزة و يكفي أن أغلب البرامج المسوقة موجهة لهتين المنصتين، لكن ثم لكن :) لم أنجذب إليهما و لم أشعر بنفس الغنى البرمجي الذي تتيحه منصة جنو/لينوكس و في النهاية زاد إحساسي بالسعادة لأنني اخترت الإنتقال إلى منصة جنو/لينوكس يوما. نعم يمكنني القول بان منصة جنو/لينوكس هي أفضل منصة برمجية لكن لا ينطبق نفس الأمر بالنسبة للإستخدام المكتبي اليومي، ليس لأنها تعاني من خصاص ما بل لأنها ستدفعك و أحيانا ستفرض عليك تعلم أشياء جديدة أنت (من منظورك كمستخدم) في غنى عنها و هدا ما لا يقبله البعض ممن يحملون شعار "لا تجعلني أفكر!" أو "don't make me think!"

قبل أن انتقل إلى نقطة أخرى لذي ملاحظتين: الاولى ويندوز7 أفضل بكثير من Vista و أفضل من XP. و الثانية محرر Eclipse أفضل من TextMate و يعوضه في كل شيء بالنسبة لمستخدمي Mac OS X. و الملاحظة الثالثة :) خط monaco يناسب البرمجة للوضوح معالم حروفه.

يونيو 21، 2009

مقارنة بين أداء بايثون و لغات البرمجة الأخرى

قضيت الفترة الأخيرة في إختبار أداء بايثون خصوصا أداء الحلاقات التسلسلية for و مقارنته بكل من روبي (Ruby)، بيئتشبي (PHP)، جنوا سي بلاس بلاس (GNU C++)، سي شارب (C#)، جافا (Java) و سكالا (Scala)

عند تعلمي لبايثون لاحظت أن مرونته و أدائه جد قويين في مجموعة من المهام، من بينها إنشاء قاموس يحتوي على 1 مليون مفتاح و قيمة في ثانية واحدة و أشياء أخرى. و أثناء حلي لمشاكل المشروع Euler Project لاحظت بعض البطء في الحلاقات التسلسلية for لكن لم أعر الأمر أي اهتمام لأن حاسوبي يعتبر من الديناصورات (P4 2.4Ghz) و عند تجربتي لنفس الكود على حاسوب آخر (Core 2 Duo 2.4Ghz) كان ينجز نفس المهمة 3 مرات أسرع.

و بعد وضعي للمشكلة رقم 5 تفضل الصديق محمد الجوهري بوضع الحل بلغة C# و عند تجربتي لحله تفاجأت من السرعة التي يتم العثور بها على الحل. قرأت الكود و جربته أكثر من مرة محاولا العثور على شيء يميزه، لكنه عادي. من هنا انطلقت رحلتي لفهم ما يجري في الحلاقات التسلسلية for في بايثون 2.6.2

الكود الاول:
في البداية كان الكود كالتالي و يستغرق 55 ثانية ليجد الحل:
n = 20
divs = [x for x in range(n, 0, -1)]
i = 0

while True:
    i = i+1
    nop = False
    for x in divs:      
        if (i*n) % x != 0:
            nop = True
            break

    if nop == False:
        print "\nThe answer is ", i*n
        break

هذا الكود رديء، و غيرته ليصبح كالتالي و هو يستغرق 40 ثانية:
i = 20
done = False
seq = range(2, 20+1)

while not done:
    for j in seq:
        if i % j != 0:
            break
        elif j == 20:
            done = True

    i += 20

print i-20

ثم قرأت حول طرق تحسين كود بايثون و وجدت من ينصح باﻹستخدام الدوال التالية lambda, map و filter بدلا من الحلقة التسلسلية for و كانت النتيجة كالتالي:

الكود الثالث يستغرق 5 دقائق و 34 ثانية:
i = 20
done = False
seq = range(2, 20+1)

while not done:
    if len(filter(None, map(lambda y: i % y == 0, seq))) == len(seq):
        done = True
        break
    i += 20
  
print i

الكود الرابع يستغرق هو أيضا 5 دقائق و 34 ثانية:
i = 20
done = False
seq = range(2, 20+1)

while not done:
    if False in map(lambda x: i % x == 0, seq):
        pass
    else:
        done = True
        break
    i = i + 20

print i

الكود الخامس: ثم بعد ذلك قمت بإدراج الكود الثاني داخل دالة و أصبح على الشكل التالي و يستغرق 24 ثانية:

def euler5():
    i = 20

    seq = range(2, 20+1)
    while True:
        for j in seq:
            if i % j != 0:
                break
            elif j == 20:
                return i

        i += 20

print euler5()



المهم ماذا يجري هنا؟ و لما كل هذا؟
بايثون يعتبر من لغات البرمجة التي تعتمد على التحديد الدينامكي أو المرن للمتغيرات (Dynamically Typed)، بمعنى أن المتغيرة a يمكن أن تحتوي على قيمة رقمية ثم تتحول لتعبر عن قيمة نصية بكل سهولة أثناء عمل البرنامج. و هذا ما يتشابه فيه كل من بايثون python، روبي ruby و بيئيتشبي php. بينما تعتمد كل من لغة سي بلاس بلاس c++ و جافا java و سي شارب c# و سكالا Scala على التحديد الثابت للمتغيرات (Statically Typed). طبعا هنا أقوم بشرح مبسط كما لم أتطرق للأفضلية لأن لكل منها (الغات) نقاط قوتها و ضعفها.

المهم هو أن هذه المرونة تتم على حساب جزء بسيط في ضعف الاداء في بعض العمليات. لكن المثير هو أن ضعف أداء الحلاقات التسلسلية for أكبر بكثير من المتوقع، فما هو السبب؟

اتضح لي فيما بعد أن الفلسفة التي يتبعها مطورو بايثون هي:
1. سهولة قراءة و وضوح الكود أهم من تعقيده لأجل تحسينه (optimization) للإستفادة من أخر جزء من الثانية.
2. هنالك قانون يسمى قانون مور (Moore's law) يقول أن كفائة العتاد تتضاعف كل 18 شهر تقريا. بمعنى أنه مع تطور العتاد سيسبح الفرق بسيطا بين الكود المُحسن و العادي.
3. و إذا كانت السرعة أولوية لا غنى عنها حينها يجب على المبرمج أن يستخدم قاعدة 90/10 بحيث يُفضل أن يكتب الجزء المسرع/المحسن بلغة C أو لغة أخرى.


النقطة المهمة التي تعجبني في مشاكل المشروع يولر Euler Project هي أن كل مشكلة تقريبا يمكن حلها بطريقة تقليدية و أخرى بإستخدام بعض الذكاء و التفكير مع مراعات أن يبقى الحل بسيطا :)
و كما قال لِيُوناردو دا فينشي (Leonardo da Vinci) "البساطة هي منتهى التعقيد"

و هذا هو ما قمت به في الحل الثاني للمشكلة رقم 5 بحيث تظهر النتيجة في جزء من الثانية.


لكن كيف هو أداء باقي لغات البرمجة الأخرى في إيجاد الحل للمشكلة رقم 5؟
1. روبي Ruby 1.8.7 أوجد الحل في دقيقتين و ثلاثين ثانية (2min 30s):
def GetResult()
    n = 20
    i = n
    while true do
        for j in 2..n do
            break if i % j != 0
            if j == n
                return i
            end
        end
        i += n
    end
end

puts GetResult()

ثم بعد تعديل الحلقة التسلسلية for أوجد الحل في دقيقتين و ثانية (2min 1s)
def GetResult()
    n = 20
    i = n
    seq = 2..n
    while true do
        for j in seq do
            break if i % j != 0
            if j == n
                return i
            end
        end
        i = i+n
    end
end

بيئيتشبي PHP 5.2.9 أوجد الحل في 21 ثانية:
$i = 20;
$n = $i;
$Done = false;
while (! $Done) {
    for ($j = 2; $j <= $n; ++$j) {
        if ($i % $j != 0) { break; }
        else {
            if ($j == $n) { $Done = true; }
        }
    }
    $i = $i + $n;
}

print $i - $n . "\n";

من خلال ما سبق يتضح أن php أسرع من python و ruby. و كلها تصنف كلغات ديناميكية النوعية.

لكن كيف هو أداء اللغات الثابتة النوعية ك C#, Scala و GNU C++ ؟ الجواب هو إنها فائقة السرعة مقارنة مع اللغات الديناميكية المجربة أعلاه.

بالنسبة لجنوا سي++ (GNU C++) أوجد الحل في ثاتية و نصف!:
#include <cstdlib>
#include <iostream>

using namespace std;

int main(int argc, char *argv[])
{
    int i = 20;
    bool Done = false;
    while (!Done) {
     for (int j = 2; j <= 20; ++j)
     {
         if (i % j != 0) break;
         else
         {
            if (j == 20) Done = true;
         }        
     }
     i += 20;
    }
   
    cout << endl << i-20 << endl;
    return EXIT_SUCCESS;
}

بالنسبة لسي شارب C# أوجد الحل في ثانيتين. (بإستخدام كود الصديق محمد الجوهري)

بالنسبة لجافا (java) أوجدت الحل في ثانية و ثمانية أعشار الثانية (1.8s)
public class Main {
    public static void main(String[] args) {
        int i = 20;
        boolean Done = false;
        while (!Done) {
            for (int j = 2; j <= 20; ++j) {
                if (i % j != 0) {
                    break;
                } else {
                    if ( j == 20) {
                        Done = true;
                    }
                }
            }
            i=i+1;
        }

        System.out.println(i-1);
    }
}

و أخيرا لغة سكالا (Scala) التي أوجدت الحل في ثانيتين و أربعة أعشار الثانية (2.4s)
def euler5(): Int = {
  var n = 20
  var i = n
  var done = true
  while (done)
  {
    var j = 2
    var iter = true
    while (iter && j < n+1)
    {
      if (i % j != 0) iter = false
      else j += 1
    }
    if (iter == true) done = false
    i += n
  }
  i-n
}

println(euler5())

كما نلاحظ فإن اللغات دات التحديد الثابت تعرض الحل في حدود ثانيتين، بينما اللغات دات التحديد الديناميكي تسغرق بعض الوقت لتصل إلى النتيجة. هذه الفرق عند إنجاز بعض المهام أو في بعض الحالات قد لا يعجب البعض، لكن في أغلب الأحيان لن تواجهوا هذا النوع من المتطلبات (السرعة أولا و أخيرا) و ستجدون في اللغات دات التحديد الديناميكي المرونة التي ستسمح لكم بتطوير برمجياتكم بسرعة أكبر و مدة أقصر. لكن إن صممت يمكن إعتبار Scala بديل أفضل لأنها تجمع بين مجموعة من المزايا و أنماط البرمجة.

الخلاصة:
1. أكتب دائما كود بسيط و تسهل قرائته.
2. الحلاقات التسلسلية for تستهلك الكثير من وقت المعالج، و يمكن تحسينها بإتباع بعض النصائح.
3. إدا كانت السرعة جد ضرورية في منطقة معينة من برنامجك حينها قم ببرمجة ذلك الجزء بلغة سي أو سي ++
4. سيحصل بايثون على آلة إفتراضية محسنة (LLVM) ستجعل أداءه مشابه للغة سي و سي++


تحديث 1: قمت بتجربة الكود الخامس بإستخدام الإصدار الجديد لبايثون 3.1 الذي توصل إلى الحل في 32 ثانية. (مازل أبطأ في هذا الإختبار لكن أفضل من الإصدار 3.0)

أبريل 09، 2009

لغة البرمجة Scala

مؤخرا دارت نقاشات مطولة بين مطوري خدمة التويتر (Twitter) بخصوص الإنتقال إلى لغة البرمجة Scala أو البقاء على Ruby. و الصراحة أكره السماع عن أية لغة برمجة جديدة أو عن إصدار جديد للغة غير متوافق مع السابق. يكفي وجود أكثر من 140 لغة برمجة متداولة حاليا. لكن ما باليد حيلة، فقاعدة الحياة بسيطة "التطور/التأقلم أو الإنقراض".

Scala لغة برمجة حديثة (ظهرت في 2004) مفتوحة المصدر، متعددت النمادج/المستويات (multi-paradigm) صُممت خصيصا لتمزج بين البرمجة المعتمدة على الكائنات (Object-oriented programming) و البرمجة المعتمدة على الوظائف (Functional programming) و هي حاليا في إصدارها 2.7.3. كما أنها تعمل على الألة الإفتراضية للغة جافا (JVM) و متوافقة مع برامجها (من السهل إستيراد وحدات الجافا)



 و هذا الصباح قضيت بعض الوقت أكتشف Scala و أقارن بينها و بين بايثون. إليكم بعض الأمثلة:

1. رسالة الترحيب بـ Scala

object HelloWorld {
  def main(args: Array[String]) {
    println("Hello, world!")
  }
}

HelloWorld.main(null)


و مقابله بـ Python

class HelloWorld():
  def main(self):
    print "Hello, World!\n"

x = HelloWorld()
x.main()



2. حساب مجموع الأعداد التي تم إذخالها عند سطر الأوامر بـ سكالة:
object Main {
  def main(args: Array[String]) {
    try {
      val elems = args map Integer.parseInt
      println("The sum of my arguments is: " + elems.foldRight(0) (_ + _))
    } catch {
      case e: NumberFormatException =>
        println("Usage: scala Main <n1> <n2> ... ")
    }
  }
}

Main.main(Array[String]("1", "2", "3"))
 


و مقابله في بايثون:
import sys

class Main():
  def main(self, args = sys.argv[1:]):        
    try:
      elems = 0
      for x in args: elems += int(x)
      print "The sum of my arguments is:", elems, "\n"
    except ValueError:
      print "Usage: python 08.py <n1> <n2> ..\n"
    
x = Main()
x.main(["2", "3"])
 



3. معالجة بسيطة لمعطيات سطر الأوامر

/** Basic command line parsing. */
object Main {
  var verbose = false

  def main(args: Array[String]) {
    for (a <- args) a match {
      case "-h" | "-help"    =>
        println("Usage: scala Main [-help|-verbose]")
      case "-v" | "-verbose" =>
        verbose = true
      case x =>
        println("Unknown option: '" + x + "'")
    }
    if (verbose)
      println("How are you today?")
  }
}

Main.main(Array[String]("-v"))


و مقابله في بايثون:
# Basic command line parsing.
import sys

class Main():
  verbose = False
  def main(self, args = sys.argv):
    for a in args:
      if a in  ["-h", "-help"]:
        print "Usage: python 06.py [-help|-verbose]\n"
      elif a in ["-v", "-verbose"]:
        self.verbose = True
      else:
        print "Unknown option: '" + a + "'\n"                
      if self.verbose:
        print "How are you today?\n"

M = Main()
M.main(["-v"])


و أثناء بحثي عن أمثلة في موقع لغة سكالة وجدت أمثلة جد متقدمة تظهر إمكانتيات هذه اللغة خصوصا تلك التي تتعلق بتطبيقات الويب. كما إتضح لي أنه من الممكن الإستفادة من الواجهة الرسومية Qt من خلال  Qt-Jambi و سوف أجرب ذلك لاحقا.

عموما، أجد لغة سكالة مثيرة للإهتمام و ربما هي جافا معصرنة :). هل سأنتقل إليها يوما؟ ربما سأجربها لحقا في برامج صغيرة لأرى هل تسرع من وقت البرمجة أم لا.