‏إظهار الرسائل ذات التسميات kalimat. إظهار كافة الرسائل
‏إظهار الرسائل ذات التسميات kalimat. إظهار كافة الرسائل

الثلاثاء، 25 مارس 2025

ما الجديد في كلمات، إصدارة فبراير 2025؟

 رسومات بطريقة Double buffering

 كانت الاطياف sprites هي الوسيلة الوحيدة للحركة والالعاب في كلمات، لكن الآن اضفنا وسيلة اخرى شبيهة بمكتبات الالعاب المعروفة عن طريق روتينات جهز.الرسم،() ابدأ.الرسم()،  انه.الرسم() ، ويستخدمون كالآتي:

جهز.الرسم()
كرر :
    ت = ابدأ.الرسم()
    -- ارسم ما تريد هنا على شاشة خاوية
    -- ت هو الوقت من الكادر السابق (time since past frame/ delta_t)

    انه.الرسم()
تابع 

هناك مثال لهذا الاسلوب مرفق مع كلمات هو cat chasing mouse


 

ألطف على الCPU

  جاءني قديما شكاوى من المستخدمين ان تنفيذ برامج كلمات يرفع جدا من صوت مروحة الCPU في اجهزتهم، وقد احتجت لتفتيش الكود الخاصة بالvirtual machine سطرا سطرا حتى عرفت سبب المشكلة (وقمت بحله).

 أمر ارسم.نص

 الطريقة الوحيدة لعرض النصوص في كلمات كانت دوماً أمر اطبع ، الآن نضيف امر ارسم.نص الذي يتيح امكانيات اكثر بكثير. صيغته كالآتي:

ارسم.نص النص في (س، ص)، اللون، الحجم، الخط

مثلاً

ارسم.نص "مرحبا" في (100، 100)، 7، 12،  "Tahoma"

 

كالعادة اللون والحجم والخط اختياريون 

 


(غالبا) اخطاء جديدة
 
لم انشر اصدارات لهذا المشروع منذ 2013 (عشر سنوات متوقفة عن النشر ولاتزال لا مثيل لها!)، لذلك اتوقع bugs يمينا ويساراً. سوف يسعدني قراءة تقارير بالأخطاء من المستخدمين لو كان هناك من لا يزال يقرأ هذه المدونة.
 
هل هذا كل شيء؟ أليس هذا بقليل؟

انا فقط انفض التراب عن هذا المشروع، لم أبدأ التطوير الحقيقي بعد. 
 
ولكي أكون صريحا معكم، المشاكل التي جعلتني اوقف مشاريعي لم تحل بعد، وقد تجدوني بعد اسبوع مثلا عدت للانغلاق من جديد.
 
لو فعلت هذا سيكون بالرغم مني. انا احب النهضة :(
 
هذا هو موقع اللغة: https://kalimat-lang.com

الخميس، 30 مايو 2013

A Prolog for my Kalimat

I've tried twice to add program analysis to Kalimat, and each time I had trouble. Program analysis is collecting data about the program to use for IDE features like 'go to definition', autocomplete, 'find usages', and so on.

Basically this operation has 3 parts:
  • Regularly parse the code in the editor windows (for speed most IDEs reparse only the changed parts of the code).
  • Update a program database that holds information like what methods are defined in each class, where is some variable declared,...etc
  • When you need to do some action like 'go to definition', query that database
Let's ignore part #1 and focus on updating and querying the program database. The first time I implemented it the solution was very ad-hoc: I stored things in hashtables, queried them with loops, and the code was very tedious, repetitive, and error-prone.

This was clearly not good, so I decided to use a real database: SQLite is a lightweight database engine that can be easily embedded in other programs, and also has the option to store data in memory instead of on disk. Perfect. In the second iteration the code for 'go to definition' looked something like this:

QSqlQuery q;
q = progdb.q("SELECT defining_token_pos, function_definitions.module_filename "
   "FROM function_definitions JOIN definitions ON definitions.id=function_definitions.def_id AND "
   "definitions.module_filename = function_definitions.module_filename WHERE (definitions.module_filename IN (SELECT imported FROM "
   "module_imports WHERE importer=?) OR definitions.module_filename=?) AND definitions.name in (SELECT lexeme FROM tokens WHERE pos <=? "
   "AND pos + length >=?)", filename, cursorPos, cursorPos, filename);
if(q.next()) { 
   docContainer->OpenOrSwitch(q.value(1).toString());
   ((MyEdit *)currentEditor())->;jumpToPos(q.value(0).toInt()); return;
}

It might look complex but the idea is simple, what the query's trying to say is "Find the filename and position of the function definition that has the same name as the name under the cursor. When looking for a function definition, look in the current module or any module imported by the current module".

This works, but has a lot of drawbacks

1- The query isn't recursive. If the current file imports module A, the IDE will look there for definitions, but if we import 'A', and 'A' imports 'B', the IDE will not look there. We want all modules reachable from the current module to be found.

SQL isn't very friendly with recursion so I probably would've had to implement recursion myself with C++ code.

2- The bigger problem is that the SQL here is too complex. Sure it beats loops and hashtables but it's still big, and that's only for 'go to definition'! How about when I add public and private definitions? namespaces?

What I need is a reasoning system. Luckily there's one: Prolog.

Actually I don't need the full Prolog language; a subset of Prolog has been created to support complex queries (much more complex than SQL), this subset is called Datalog, and it's successfully used in program analysis in many research projects.

I set out to find a nice Datalog implementation for my aims, but didn't find something sufficient: there's an excellent library called BDDBDDB but it's in Java, and there's another one called simply 'Datalog' written in C and Lua that seemed not straightforward to integrate with Kalimat (nb: Kalimat is written in C++/Qt).

Then I looked at open source Prolog implementations like SWI or Ciao, and while very powerful and fast, they didn't seem easy to integrate with a standalone C++ app, especially an app like Kalimat that has to run on both Windows and Linux. I might be wrong but I felt it wasn't easy.

So what did I do? Why, created a new Prolog implementation of course! It's called SmallProlog (and nicknamed 小さい Prolog), specially designed for integration with C++/Qt.

Using Prolog from C++ has never been easier:

PrologEngine engine;
engine.load(prologCode);
engine.call("predicate", arg1, arg2, [](QMap<QString, QVariant> sol){ 
 // do stuff with each solution
});

The expression [ ](QMap<QString,QVariant> sol){ ... } is a lambda expression, a new feature of C++. You define here an anonymous function that gets called whenever the Prolog engine finds a solution. The 'solution' will be a map from variable names to their values.

Under the hood the engine stores facts in a SQLite database, but this time the queries are generated for us from a higher level and more expressive language.

So how does 'go to definition' look now?

usagesOfProc(DefModule,DefPos,UseModule,UsePos):-
   
visibleFrom(UseModule,DefModule),
    procInvokation(UseModule,Name,UsePos),
    proc(DefModule,Name,DefPos,_,_,_)
    .
  
visibleFrom(Mod1, Mod2):-
    visibleFrom2(Mod1,Mod2,[Mod1]).
visibleFrom2(M1,M1,_).
visibleFrom2(M1,M2,Visited):-
    imports(M1,Temp),
    not(member(Temp,Visited)),
    visibleFrom2(Temp, M2,[Temp|Visited]).

The second predicate, called visibleFrom, can take a Kalimat module and give us all the other modules reachable (recursively) from the first module, including itself.

It can also work in the opposite direction: given a module it can give us all modules that 'see' it. The power & elegance of Prolog at work.

The first predicate, called usagesOfProc, takes a module & a position of a function call, and gives us another module and position where the function is defined.

It can also work in the opposite direction: given a function definition, it can give us all files and positions where the function is called.

So when I implemented 'go to definition' I was also implementing 'find usages'; write a feature and get one for free! The power & elegance of Prolog at work.

Aside: I wish the makers of Prolog implementations marketed them as languages for more than industrial and vertical market type applications, and made them trivial to integrate with all types of other languages.

Also, to help myself with debugging, I made a Prolog console where I can query the Kalimat program database live, while the IDE is running:



It's an experiment, and it's still unfinished so I don't know if it'll work as I desire or not, but I'm very excited about it. Aren't you? :)

الاثنين، 11 مارس 2013

Kalimat: an educational programming language with a twist

I've been working on a language for teaching programming to children. One sentence summary: It looks like the child of QBasic and Google's Go, with influences from other languages.



The  syntax and programming environment are geared towards educational use, but the VM on which programs run (called SmallVM) has support for microthreads, CSP channels, and an FFI. The language itself has things like lambda expressions, a limited form of destructuring, and opt-in tail call elimination.

This article will be divided into two main parts, the first of which discusses Kalimat as an educational language for beginners, and the second shows some of its more advanced features, talks about the implementation, and mentions some ideas about taking the language forwards.

A little background

Kalimat started as an Arabic-based programming language, but I've decided to turn it into a bilingual one; with a compiler switch you can have Arabic or English-based syntax with a corresponding IDE and standard library. I'm doing what I can to make both languages equal citizens, but Arabic still has a higher precedence since the goal of Kalimat is to help the Arab-speaking world learn programming, and to provide a means of "computational empowerment" to non-programmers.

Kalimat was made bilingual because:
  • Often, the novelty of an Arabic-based language dominated the conversation when I wanted to talk about the design and features of the language itself. Having an English version would make it simpler to talk about the technical details.
  • I wanted to interact more with the global PL community, the majority of which are English speakers.
  • To help with scientific studies about teaching programming with PLs in native vs. non-native human language.
  • As a bonus, working with an English version of Kalimat showed me some verbosity in the original syntax which I hadn't noticed since I was too used to the original syntax I had designed.

The language can be downloaded from here, with pre-built English and Arabic versions for Windows. Non-Windows users can check out the source and build it with Qt Creator and the Qt library (version 4.8.x), but they would need to uncomment the following line in both of the files 'kalimat.pro' and 'smallvm.pro' to Enable English syntax/IDE:

#DEFINES+= ENGLISH_PL

Moving on....

Basic syntax

This is a word-count sample in Kalimat:

read "Enter some text:", str 
words = split (str, " ")
result = {}
for word in words:
    if not containsKey (result, word):
        result[word]= 1 
    else:
        result[word]= result[word]+ 1 
    done 
loop 

for pair in result:
    print key pair, " : ", value pair 
loop 

Looks kind of Pythonic, and the code is self-explanatory I think! We have the basics: Simple input and output, iteration, dictionaries (arrays are also supprted), and conditionals.

Here is another quick example:

proc lines (a):
    for i = 1 to a :
        drawLine(random (800), random(600))-(random(800), random(600)), random (16)
        wait (100)
    loop 
end 

lines (200)

In Kalimat a user can define a proc or a func, with the latter being able to return values to the caller (similar to the Sub/Function dichotomy in Microsoft's *-Basic languages). In my opinion this makes the language easier to teach since we can explain procs as 'pieces of code with a name', and worry later about passing parameters and using return values. Experimentation would tell if this was the right decision.

As seen from the drawLine statement, Kalimat has graphics commands that were taken as they are from 80s BASIC, we also have drawPixel, drawCircle, drawRect, drawImage and drawSprite.

We have classes and objects in Kalimat too:

class Point :
    has x, y 
    responds to draw ()
end 

response Point p to draw ():
    drawPixel (x p, y p)
end 

p = new Point having x = 120, y = 150 
p : draw ()

Like the proc/function division, an object can respond to a message sent to it, or it can reply with a value.

The syntax is a little verbose but I have a hypothesis that it would pay off when teaching OOP to beginners. In my brief period of teaching OOP (to college students) I have often found that the syntax of a language like C++ has too many implicit meanings (what 'this' means, what happens in a line like a.b(c)...etc) and that when the students see this syntax they need constant reminding of what it actually represents.

I've tried to make the stuff that happen behind the scene explicit in the syntax, things like 'I'm sending a request to the object, the object shall respond, the response has a reference to the actual object...). When they have fully grasped the semantics the learners can move on to more mainstream languages (or an additional, more streamlined syntax, could be added to Kalimat).

Combining graphics, OOP and Kalimat's sprite engine; we can remake the QBasic classic Gorilla example. The code for that example (and others) is bundled with Kalimat's distribution, or it can be seen here.

Finally, Kalimat has features to help specifically with teaching:
  • The 'Edit' menu has a 'Copy as HTML' feature to help post code in online tutorials and the like. The examples in this blog have been formatted that way (but some minor annoyances in spacing had to be manually corrected; I hope I can fix those annoyances soon).
  • The 'Program' menu has a feature called the monitor which provides an animated trace of a program, while showing the call stack in the 'Variables' tab at the bottom.
  • The 'Test' menu has the option of showing the SmallVM IL generated from source, which could be useful if someone wants to use Kalimat for teaching compilers. It's planned to later add features for visualizing basic blocks and control flow in methods, mainly to help me debug the VM; but could also be used in teaching compilers.
This concludes our brief tour of Kalimat as an educational language, now we move on to concurrency and other more advanced features.

Concurrent processes

Those five forms are the main ingedients for working with processes:

launch procInvokation ()
c = channel ()
send val to c 
receive var from c 
select :
 send 123 to c1 :
   -- statements
 or receive x from c2 :
   -- statements
done

Processes are scheduled internally by the VM and are independent from operating system processes or threads. The launch statement starts a process, the channel() function creates a new channel, send, receive and select statements enable communication between processes using channels as a data exchange/synchronization mechanism.

Both sending and receiving are synchronous (a sending operation blocks until another process is ready to receive from the channel and vice versa), and the select statement takes a set of alternative channel operations and applies the first operation that is ready to run, along with its associated sub-statements; if more than one operation were ready at the same time one operation is chosen randomly from among them.

(If you're familiar with Google Go, then all of this is old news to you).

Why were these features added? Again based on some hypothesis I have about teaching programming. The goals were:
  • To enable children to create games and animated scenes where multiple agents move and behave independently
  • To make GUI programming easier (all GUI widget events are represented by channels, you could model a sequence of GUI interactions with sequential statements: receive some event from channel1, then receive another from channel2...)
  • Since we seem to be entering an age of concurrent programming, it doesn't hurt to have a language where it's easier to teach the concepts of concurrency

I think it's interesting that channels support Kalimat's iteration protocol, so you can say

for someVar in myChannel:

..and have the system iteratively receive data from the channel until it's closed. This feature encompasses the capabilities of something like Python's generators (or yield return from C#) but I think it's more powerful than either of them; for example, from what I've seen it's somewhat cumbersome to use yield to recursively create an iterator for a tree in Python, however in Kalimat it's very natural:

for elem in enumerateTree (t):
    print elem 
loop 

proc enumerateTree (t):
    chan = channel ()
    launch traverseTree (t, chan)
    return chan 
end 

proc traverseTree (t, chan):
    traverse (left t)
    send data t to chan 
    traverse (right t)
end

What we did was a little bit of glue code, then a traditional tree traversal, sending whatever we visit to the channel.

Simple destructuring

We have syntax to test if some given data has a given structure and to extract data from that structure. In this example we'll be testing if a given value is an array, map, or object:

proc showName (x):
    if x ~ [?first, ?last]:
        print first, " ", last 
    else if x ~ {"firstName"=>?x, "lastName"=>?y}:
        print x, " ", y 
    else if x ~ Person having firstName = ?x, lastName = ?y :
        print x, " ", y 
    done 
end

Preceding a variable name by ? extracts the corresponding component into the variable, otherwise the component is tested for equality against the variable (using the same ?var twice destroys the first value, this feature is not really pattern matching/unification). Matching keys in a map cares only about the subset of keys being tested (i.e other key/value pairs could exist and the match would succeed), the same goes for testing fields in objects. Matching on arrays like this x ~ [a, b, c] tests against the exact array count, but x ~ [a, b, c, ...] can successfully match a larger array.

Tail call elimination

The statement delegate to someInvokation() can be used inside a procedure or a function in Kalimat to properly tail-call some routine. This feature was created in case an educator wanted to teach certain styles of programming, and to make the VM extensible (for example to make it possible to support continuations if so desired).

Parser generation

Kalimat has a built-in parser generation engine (this was made with the intention of using Kalimat to teach computer science and not just programming, and to enable the creation of natural language interfaces, text adventure games,...etc by kids).

For example writing this code

rules expression :
 
expression = term:a "+" expression:b => ["+", a, b]
or term:a "-" expression:b => ["-", a, b]
or term 

term = number:a => toNum (a)
or "(" expression:a ")" => a 

number = digit:a number : b => a + b 
or digit 

digit = from "0" to "9" 
 
end

...would provide a function called expression that takes a string and returns a parse tree for simple arithmetic expressions. The capability uses PEG notation with a memoizing packrat parser. Unfortunately the generated parsers can't currently handle left recursion (thus the above grammar actually has a subtle mistake because it treats subtraction as right-associative). I've read the various papers about handling left-recursion in PEG's, but I have yet to understand the techniques :)

A function-plotting sample application that demonstrates this feature is bundled with Kalimat, or could be seen here.

There are two interesting additions that could be made to the parsing engine:
1- Adding the ability to parse any enumerable stream of data, not just a string, and to pattern match against data. This would be like OMeta and its various derivatives.

2- Since channels are enumerable, this would make it possible to parse anything that could be sent to channels: imagine the ability to write grammars for GUI interaction models, sequences of packets from the network, routing web requests, and so on; basically enabling the creation of grammars for things for which we now create state machines.

If features #1 and #2 above were well-implemented I think it would give way to an exciting, highly declarative style of programming.

Implementation

Kalimat code runs on a custom-made VM called SmallVM. It's a simple stack-based machine that runs concurrent processes using an internal scheduler. It currently uses two threads: one for code execution and the other for GUI.

Because Qt does not allow GUI operations to run outside the event handling thread; the GUI thread also has an interpreter instance. A process that needs to do a UI operation migrates to the other interpreter, does its job, then migrate back to the main execution thread. There are plans to have multiple execution threads and to multiplex the execution of processes among them (like Erlang or Go).

An ambitious project would be to hook the VM scheduler to libuv and have the ability to schedule tasks Node.js style but without the callbacks, since the VM would suspend and resume processes automatically.

Performance wise, I could describe SmallVM as "somewhat satisfactory for its intended goals". On my machine, executing fib(30) takes about one second in Python but 3 seconds in Kalimat. On another dual core machine the time for Python is still one second, but for SmallVM it becomes about 1.4 seconds. I have yet to run more scientific benchmarks.

The VM has a simple FFI built using libffi; it supports calling C functions and having callbacks to Kalimat functions. The callback feature is currently still in development since it has a complicating issue: what happens if C code calls a Kalimat procedure, then that procedure waits on a channel until another process sends it something ?

To make the above case work invoking a callback has to run a secondary VM execution cycle, not just run the code of the callback procedure. Even running in multiple threads wouldn't completely solve the problem. The code to handle these cases is already in the VM, but not really tested and is expected to be buggy for now.

This applies to Kalimat in general: I think I might have some good ideas for designing a PL but I'm not so good at implementation. The language has its fair share of bugs and quirks, and now with the English based IDE the quirks have increased (expect to see the occasional untranslated error message or right-to-left dialog box for the time being). However I'm pushing on, and I hope the product keeps getting better over time.

Thanks for reading about my small programming language; I hope something from it got your interest!

الثلاثاء، 26 فبراير 2013

ما الجديد في كلمات، إصدارة فبراير 2013

بروتوكول للتكرار

تدعم كلمات الآن إمكانية "بروتوكول التكرار"، هذه الميزة تجعل التعامل مع البيانات أسهل ولها قوة خاصة حين تعمل مع إمكانية القنوات. التفاصيل في هذا المقال.


أسرع :)

مرة أخرى نقترب من هدفنا (مضاهاة سرعة لغة بايثون أو التفوق عليها) بالمزيد من التحسينات في أداء برامج كلمات.

نسخة انجليزية

يمكن الآن استخدام كلمات كلغة برمجة انجليزية أيضاً. ليس معنى هذا على الإطلاق أنني تركت فكرة لغة البرمجة العربية! كلمات العربية مازالت هي المنتج الأساسي، لكل النسخة الانجليزية قد يكون لها بعض الفوائد العملية عند الحاجة، مثلاً:
  • استخدامها في تجارب علمية توضح الفرق في سهولة التعليم بين تعليم البرمجة بلغة برمجة عربية ولغة أخرى مشابهة لكن انجليزية، مع تقليل الفوارق الأخرى بين اللغتين قدر الإمكان.
  • الرد على من قد يقول أن كلمات لا يوجد ما يميزها سوى أنها عربية؛ بإظهار مزاياها حتى ولو كانت انجليزية
  • ...وهكذا

النسخة الإنجليزية تستخدم نفس الكود الخاصة بالنسخة العربية، ويتم بناؤها بفتح ملف kalimat.pro وإزالة علامة # في السطر الذي نصه:
#DEFINES += ENGLISH_PL

أرجو ملاحظة أن النسخة الانجليزية ليست مكتملة، وأنه قد يكون بها مشاكل في الإصدارة الحالية

يمكن تحميل أحدث إصدارات كلمات من موقع تطويرها على جوجل كود:
أو من موقع اللغة الرسمي:

ويمكن تعلمها من دليل الاستخدام الذي يمكن تحميله من الرابط التالي، أو من أي من المواد التعليمية الموجودة على موقعها

نتمنى لكم برمجة ممتعة مع كلمات!

الأحد، 17 فبراير 2013

بروتوكولات التكرار في لغة البرمجة كلمات

مصطلح بروتوكولات التكرار هو محاولة مني لتعريب iteration protocols، وهي إمكانية موجودة في لغات برمجة كثيرة مثل Java, #C، بايثون، وغيرها. (كلمة iteration معناها حرفياً تكرار، لكني لست متأكداً من هذا التعريب وأريد أن أراجعه).

هذه الإمكانية هي التي تسمح لك أن تقول مثلاً في سي شارب:
foreach(int x in p) {
  // code  
}

ثم تجد هذه الحلقة التكرارية تعمل مع أي نوع بيانات: مصفوفة، شجرة، قائمة، ...الخ، وتسمح لك أيضاً أن تجعل فصائلك الخاصة تدعم حلقات foreach بشرط أن تلتزم فصيلتك تلك بمواصفات معينة (بروتوكول).

وقد أضفت iteration protocol في كلمات (في نسخة قيد التطوير ولم تصدر بعد، لكن أتمنى طرحها قبل نهاية فبراير)، وهنا أتكلم عن هذه الإضافة. أولاً، صار في كلمات حلقة لكل/في:
 م = [1، 2، 3، 4]
لكل أ في م :
    اطبع أ 
تابع

 هذه الإمكانية تسمح بالمرور على عناصر المصفوفات، والنصوص، والقواميس. في حالة القاموس يكون العنصر الذي يأتيك هو كائن به خاصية اسمها مفتاح وخاصية اسمها قيمة ، ويمكن استخدامه هكذا:

ق = {"حار" => "بارد"، "كثير" => "قليل"، "قوي" => "ضعيف"}
لكل أ في ق :
    اطبع "عكس "، مفتاح أ، " هو "، قيمة أ 
تابع 

بالنسبة للبروتوكول نفسه: لو أردت أن تصمم فصيلة بحيث يمكن المرور على عناصر كائناتها بأمر لكل/في، فستحتاج فصيلتك أن يكون فيها رد (أي method تعود بقيمة) اسمه المعدد ، يعود بكائن خاص تستخدمه حلقة لكل. كيف تستخدمه بالضبط؟ لو قلت مثلاً:

لكل أ في ب: 
    -- أوامر
تابع 

فإن الأمر يتم تحويله داخلياً إلى ما يشبه الآتي:
م = ب : المعدد()
كرر مادام م : تقدم()
    أ = م : القيمة.الحالية()
    -- أوامر
 تابع 

إذاً لكي يعمل المعدد لابد أن يكون فيه ردين، اسمهما  تقدم و القيمة.الحالية ، ويحققان معاً الشروط الآتية:
  • في البداية يكون المعدد "قبل أول عنصر"، معنى هذا ببساطة أننا في تلك الحالة لو نادينا تقدم وكان هناك عناصر، سنصبح عند أول عنصر.
  • تقدم تحاول الذهاب للعنصر الذي عليه الدور، تعود بالقيمة صحيح في حالة وجود عنصر وإلا تعود بالقيمة خطأ
  • القيمة.الحالية تعود بالعنصر الحالي (الذي وصلنا إليه عن طريق تقدم)، إن كان هناك عنصراً موجوداً.
سوف يلاحظ مبرمجو لغة سي شارب أن البروتوكول الخاص بكلمات هو نفسه بروتوكول تلك اللغة. نعم هذا صحيح :)

لكن هناك شيء خاص بكلمات هنا: القنوات تدعم بروتوكولات التكرار.

هذا شيء مهم جداً، لذلك سأكرره: القنوات تدعم بروتوكولات التكرار!

(إن كنت لا تعرف بعد ما هي القنوات، يمكنك أن تطلع على هذا الجزء من توثيق كلمات الخاص بالبرمجة المتوازية)

ما أهمية هذه المعلومة على أية حال؟ سأقول لك: هذا يجعل أشياء برمجية كثيرة سهلة للغاية. يمكنك أن تشغل إجراءاً موازياً يقرأ البيانات من أي مصدر (قائمة ملفات، من الإنترنت، أو حتى من قنوات أخرى) ويرسل كل ما يقرأه إلى قناة، ثم في البرنامج الرئيسي تقوم بالقراءة من القناة بواسطة أمر لكل/في.

مثال بسيط على هذا، المرور على عناصر شجرة: تخيل أن لدينا فصيلة اسمها شجرة لها ثلاث خصائص: بيان، يمين، يسار. هذه هي الشجرة الثنائية العادية التي يأخذها طلبة علوم الحاسب. الآن نريد أن نفعل هذا:

لكل أ في تعديد.شجرة(ش):
    ....
تابع
سوف نكتب الآن الدالة تعديد.شجرة (كلمة تعديد هي تعريبي لكلمة enumeration):
دالة تعديد.شجرة(ش):
    ق = قناة()
    شغل تعديد(ش، ق)
    ارجع ب: ق 
نهاية

كما ترى، كل ما تفعله هذه الدالة هو صنع قناة، ثم تشغيل إجراء مواز وتقديم الشجرة الأصلية والقناة له. وظيفة ذلك الإجراء (المسمى تعديد) هو المرور على عناصر الشجرة وإرسال كل ما يمر عليه للقناة:

إجراء تعديد(ش، ق):
    إذا ش <> لاشيء
        تعديد(يسار ش، ق)  
        ارسل بيان ش إلى ق 
        تعديد(يمين ش، ق)
    تم 
نهاية

الشيء الذي يميز هذا الإجراء هو أنه لا يميزه شيء! إنه إجراء tree traversal عادي جداً جداً - مرة أخرى، مثل ما يأخذه طلبة حاسبات - كل ما يفعله هو أنه حين يجد قيمة يرسلها للقناة، ولا يوجد أي شيء عن التعديد أو البروتوكولات، لكن هذا الإجراء هو كل ما تحتاجه للمرور على عناصر الشجرة.

ماذا لو أردت المرور بطريقة مختلفة مثل breadth-first أو ربما postorder؟ غيّر الإجراء الثاني بدون أن تهتم بالبروتوكلات وما إلى ذلك. ماذا لو أردت المرور على عناصر شيء آخر؟ ربما graph؟ استخدم الخوارزميات التي تعرفها، لكن لا تنس إرسال كل عنصر  للقناة :)

توجد إمكانيات مشابهة في لغة سي شارب (أمر yield) وفي لغة بايثون (إمكانية generators) لكني أعتقد أن طريقة كلمات أقوى (المرور على الشجرة مثلاً أعقد في سي شارب/بايثون لأن أمر yield لا يعمل بنفس الطريقة في وجود recursion).

عند تصميم لغة برمجة لا يكفي أن تضع فيها باقة من الإمكانيات، ولكن لابد من التفكير في التكامل بين الإمكانيات المختلفة. أعتقد أن التكامل بين الإجراءات الموازية والقنوات وبروتوكولات التكرار يعطي كلمات قوة خاصة.

الأحد، 20 يناير 2013

ما الجديد في كلمات، إصدارة يناير 2013؟

أسرع وأسرع

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

 إمكانية تلوين الحروف عند طباعتها


الآن يمكن استخدام الدالة حدد.لون.النص لتغيير لون
 أي نص يطبع بعد استدعاء الدالة.










نوافذ منفصلة


 الآن يمكن لبرنامجك أن ينشيء نوافذاً جديدة غير نافذة التنفيذ المعتادة. يمكن الاطلاع على المثال المسمى 'نافذة منفصلة' في مجلد 'أمثلة بسيطة' المرفق مع كلمات لمعرفة كيفية استخدام هذه الإمكانية.





عودة الملفات التنفيذية

هذه الإمكانية قد عادت، وأضفنا لها بعض اللمسات الأخرى كما توضّح الصورة :)



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

الأحد، 23 ديسمبر 2012

حين تتحقق خطط المدى البعيد

حين كنت أعمل في صنع لغة كلمات سنة 2010 كانت هناك بعض القرارات التي اتخذتها في تصميم وتنفيذ اللغة. بعض هذه القرارات قد بدأت تظهر آثاره الآن!

الأحد، 16 ديسمبر 2012

رسم الصيغ الرياضية بلغة كلمات (الجزء الثاني)

الهدف من المشروع هو برنامج يرسم الصيغ الرياضية، مثلاً يقول له المستخدمم س^2 + 5 فيرسم البرنامج شكل الـparabola المعروف.
  • في الجزء الأول تحدثنا عن الإعراب، الدالة التي تأخذ نصاً مثل 12+13 وتعود بالمصفوفة ["+"، 12، 13] التي تمثل ما يسمى بشجرة الإعراب parse tree.
لو كنت قد قرأت الجزء الأول فأنت إذاً جاهز الآن لنختتم هذه السلسلة.

حساب قيمة التعبير

حسناً. لدينا الآن شجرة إعراب مثل 100 أو ["+"، س، 12]، ماذا نفعل بها؟ نريد دالة اسمها تقييم تأخذ الشجرة وتعود بالقيمة التي تعبر عنها.
  • لو كانت "الشجرة" في صورة قيمة عددية، نعود بتلك القيمة.
  • لو كانت الشجرة هي المتغير س (كقيمة نصية)، فإننا لا نستطيع أن نفعل شيئاً بأنفسنا، لذلك سوف نقدم عاملاً إضافياً parameter للدالة تقييم به قيمة س المطلوبة.
  • لو كانت الشجرة في صورة مصفوفة مثل ["+"، أ، ب] سوف نقوم بالآتي:
    • ناد الدالة تقييم مع الفرع الأيمن أ لحساب قيمته
    • ناد الدالة أيضاً مع الفرع الأيسر ب
    • نفذ العملية الحسابية وارجع بالنتيجة.
كل هذا يعطينا الكود التالية، الدالة تأخذ الشجرة "ت" وقيمة "س" المحددة من البرنامج، وتحسب قيمة الصيغة الرياضية:
دالة تقييم (ت، س):
    إذا ت ~ ["+"، ؟أ، ؟ب]:
        ارجع ب: تقييم (أ، س)+ تقييم (ب، س)
    وإلا إذا ت ~ ["-"، ؟أ، ؟ب]:
        ارجع ب: تقييم (أ، س)- تقييم (ب، س)
    وإلا إذا ت ~ ["×"، ؟أ، ؟ب]:
        ارجع ب: تقييم (أ، س)× تقييم (ب، س)
    وإلا إذا ت ~ ["÷"، ؟أ، ؟ب]:
        مقام = تقييم (ب، س)
        إذا مقام <> 0 :
            ارجع ب: تقييم (أ، س)÷ مقام 
        وإلا :
            اطبع "خطأ: قسمة على صفر" 
            ارجع ب: 0 
        تم 
    وإلا إذا ت ~ ["^"، ؟أ، ؟ب]:
        ارجع ب: أس (تقييم (أ، س)، تقييم (ب، س))
    وإلا إذا ت ~ "س" :
        ارجع ب: س 
    وإلا :
          -- لو وصلنا إلى هنا فقيمة ت هي اصلا عدد
         ارجع ب: ت 
    تم 
نهاية

لاحظ كيف استخدمنا مرة أخرى عملية المطابقة ~ لتسهيل الموضوع. الآن نحن جاهزون للرسم. أولاً نكتب إجراءاً مساعداً يرسم المحاور س، ص
إجراء ارسم.المحاور ():
    ارسم.خط (400، 0)- (400، 599)
    ارسم.خط (0، 300)- (799، 300)
نهاية

الآن سوف نكتب الدالة التي ترسم فعلاً. خطتنا كالآتي:
الدالة سوف تأخذ عاملين: من.س و إلى.س،  لكي نستطيع أن نقول مثلاً ارسم س^2 من س=-4 إلى س=4
سوف تقوم الدالة بالآتي:
  • كرر بحيث يبدأ المتغير أ بالقيمة من.س وينتهي بالقيمة إلى.س، وكل مرة نضيف قيمة صغيرة إلى أ:
    • احسب ص عن طريق استدعاء تقييم لحساب قيمة الصيغة الرياضية، واعطها قيمة المتغير س تساوي العداد أ
    • سوف نرسم خطاً صغيراً بين كل نقطتين:
      • لو كان هذا أول تكرار، فلا يوجد لدينا سوى نقطة واحدة. خزن قيمة أ في المتغير أ.قديم وقيمة ص في المتغير ص.قديم
      • لو لم يكن أول تكرار، فنحن لدينا النقطة (أ، ص) والنقطة (أ.قديم، ص.قديم). سنرسم خطاً بينهما
هذا يعطينا الكود :
إجراء ارسم.الدالة (التعبير، من.س، إلى.س):
    أ = من.س 
    أ.قديم = لاشيء 
    ص.قديم = لاشيء 
    مقياس.س = 20 
    مقياس.ص = 15 
    
    كرر مادام أ <= إلى.س :
        ص = تقييم (التعبير، أ)
        إذا ليس أ.قديم = لاشيء :
            س1 = - أ × مقياس.س + 400 
            ص1 = - ص × مقياس.ص + 300 
            س2 = - أ.قديم × مقياس.س + 400 
            ص2 = - ص.قديم × مقياس.ص + 300 
            ارسم.خط (س1، ص1)- (س2، ص2)، 4 
        تم 
        أ.قديم = أ 
        ص.قديم = ص 
        أ = أ + 0.1 
        انتظر (30)
    تابع 
نهاية

لاحظ أننا نضرب الإحداثيات في بعض الثوابت ليكون لدينا مقياس رسم، وننتظر 30 مليثانية بين كل رسمة لكي نرى الدالة وهي تُرسم بسلاسة على الشاشة. لاحظ أن هذه الطريقة تفترض بعض الخصائص للدالة المطلوب رسمها (مثلاً أنها متصلة continuous).

لا يبقى إلا ان نربط كل المكونات ببعضها!

اقرأ "الصيغة؟"، الصيغة 
اقرأ "من؟"، # أ 
اقرأ "إلى؟"، # ب 

الصيغة = تبديل (الصيغة، " "، "")
الشجرة = تعبير (الصيغة)
ارسم.المحاور ()
ارسم.الدالة (الشجرة، أ، ب)

ماذا فعلنا؟
  • قرأنا الصيغة من المستخدم (وقيم س التي سنبدأ وننتهي بها)
  • حذفنا المسافات من الصيغة
  • أعربناها لنحصل على شجرة الإعراب بواسطة الدالة تعبير
  • رسمنا المحاور
  • نادينا ارسم.الدالة وأعطيناها الشجرة وقيم من.س وإلى.س
  • ارسم.الدالة سوف تنادي تقييم باستمرار لكي تحسب قيمة الصيغة عند كل قيمة لـ س
  • ....وترسم الخطوط المطلوبة!

كم سطراً كتبنا؟ للأسف تجاوزنا حاجز المائة قليلاً: 109. لكن لو حذفنا السطور الفارغة بين تعريفات الدوال...الخ سيكون العدد أقل من المائة.

أقل من مائة سطر، تقوم بما قد يحتاج مئات الأسطر في لغات أخرى...

يمكنك عزيزي القاريء أن تجرب عمل نسخة متقدمة من البرنامج تضيف إمكانية استخدام الدوال مثل جتا، جا، أو تضيف الثابتين ط (π) و هـ (e) أوتقدم بعض المساعدة للمستخدم. لمَ لا تجرب؟ :)

رسم الصيغ الرياضية بلغة كلمات

نريد برنامجاً يأخذ صيغة رياضية ويرسمها!


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

الجزء الأول: كيف نفهم الصيغة الرياضية؟

يوجد في علوم الحاسب ما يسمى parsing (الإعراب). لو أعربنا التعبير الحسابي س^2+5 فسيكون لنا النتيجة التالية:

هذه الرسمة اسمها "شجرة الإعراب" أو parse tree، وهي تشبه الشجرة لأن لها جذراً وفروعاً. كيف نعبر عن هذه الشجرة بقيمة في لغة كلمات؟ هناك طرق كثيرة: يمكن أن نستخدم الفصائل والكائنات، أو المصفوفات، أو القواميس...فلنستخدم طريقة بسيطة: سوف تكون كل شجرة عبارة من مصفوفة من ثلاثة عناصر: العملية الحسابية نفسها، والقيمتان التي تجري عليهما العملية. مثلاً إن قمنا بإعراب التعبير "4+5" سوف نحصل على المصفوفة ["+"، 4، 5]. لو أعربنا القيمة النصية "(4+5)×2" سوف نحصل على ["×"، ["+"، 4، 5]، 2]. أي أن الشجرة هي عملية ضرب بين ["+"، 4، 5] وبين 2.

لو افترضنا أننا - بطريقة ما - لدينا دالة تأخذ نصاً من المستخدم وتعود لنا بهذه الشجرة، فإنه من السهل أن نحسب القيمة المطلوبة من الشجرة: نحسب الفرع الأيمن، ثم نحسب الفرع الأيسر، ثم نقوم بتطبيق العملية الحسابية.

لاحظ أن الإعراب لا يعود دائماً بمصفوفات..لو أعربنا مثلاً التعبير 12 فإن "الشجرة" ما هي إلا العدد 12 نفسه.

رائع. الآن علينا أن نفكر: مم تتكون الصيغ الرياضية؟ سوف نسمي الصيغة في صورتها النهائية "تعبير حسابي"، وهذا التعبير مكون من حد واحد أو أكثر. ما الذي يفصل بين الحدود في التعبير الواحد؟ علامات الجمع والطرح. أي أن التعبير يكون في صيغة حد1 + حد2 - حد3....الخ، أو مكون من حد واحد.


كيف نعبر عن هذا رياضياً؟ هناك طريقة اسمها Parse Expression Grammar أو PEG تسمح لنا أن نكتب القاعدة بهذه الطريقة:

تعبير = حد "+" تعبير       (هذه قاعدة 1#)
أو       حد "-" تعبير       (قاعدة 2#)
أو       حد                  (قاعدة 3#)

ما معنى هذا الوصف؟ معناه أن التعبير يمكن أن يكون حداً واحداً أو مجموعة من الحدود يفصل بينها + و -. مثلاً يمكن أن نبدأ بالحد "2×3". نحن نعرف أن هذا تعبير من قاعدة #3 من التعريف. ماذا عن 1 + 2×3؟ نعرف أن هذا أيضاً تعبير (من قاعدة #1 التي تقول أن حد + تعبير = تعبير، وبالتالي سمحت لنا بإضافة حد في المقدمة). ماذا عن 5 - 1 + 2×3؟ هذا أيضاً تعبير من جزء 2# من التعريف


لو شعرت أن الأمر صعب فلا تقلق: الموضوع ببساطة هو recursion مثلما تعلمت في البرمجة. المسألة هي أن التعبير يمكن أن يكون حداً أو يكون حد + حد + حد ....، أو حد + حد - حد - حد ....الخ.

ماذا عن الحدود نفسها؟ الحد هو "أس" واحد أو أكثر تفصل بينها علامات  ×، ÷

هذه كلها حدود:

5^2 × 6
6 ÷ 3
18
6^س

كيف نكتبها بطريقة PEG؟ هكذا:

حد = أس "×" حد
أو     أس "÷" حد
أو أس

وهي كما ترى نفس طريقة التعبيرات. لاحظ أن قيمة عادية مثل "18" نعتبرها أس رغم غياب علامة ^، مثلما اعتبرنا أن 12 هو تعبير رغم غياب علامة + أو -

ماذا عن الأسس؟ هي ببساطة مجموعة من واحد أو أكثر من التعبيرات الأولية يفصل بينها علامة ^. كل هذه أسس:

س^2
2^س
س^2^3
س
12

والقاعدة (كما خمنت) هي:

أس = أولي "^" أس
أو     أولي

ولكن ما هو التعبير الأولي؟؟؟ إنه رقم، أو المتغير س، أو تعبير بين أقواس:

أولي = "س"
أو     رقم
أو  "(" تعبير ")"

لم يبق ما نعرّفه سوى الأرقام نفسها. الرقم هو خانة واحدة أو أكثر (الخانة هي الرمز 0، أو 1، أو 2...إلى 9).

رقم = خانة رقم
أو خانة
خانة = من "0" إلى "9"

الرقم هو خانات لا يفصل بينها شيء. لهذا لم نقل مثلاً رقم = خانة + رقم بل قلنا مباشرةً رقم = خانة رقم....

ولكن ما فائدة كل هذا؟؟؟؟؟؟؟؟؟؟؟؟؟؟؟؟؟؟

فائدة كل هذا أن لغة كلمات بها محرك إعراب جاهز. لو قدمت له قواعد في صورة PEG سوف يقدم لك إمكانية إعراب أي نص يتماشى مع هذه القواعد. مثلما نعرّف في كلمات دالة أو إجراء أو فصيلة، يمكننا أيضاً أن نعرّف قواعداً. انظر للكود الآتية:
قواعد تعبير :
    تعبير = حد "+" تعبير 
    أو حد "-" تعبير 
    أو حد 

    حد = أس "×" حد 
    أو أس "÷" حد 
    أو أس 

    أس = أولي "^" أس 
    أو أولي 


    أولي = رقم 
    أو "س" 
    أو "(" تعبير ")" 

    رقم = خانة رقم
    أو خانة 

    خانة = من "0" إلى "9" 
نهاية 
 

اطبع تعبير ("1+2+3")

في السطر الأخير نجرب القواعد الجديدة. سوف يطبع مفسر كلمات لك القيمة......لاشيء :(

لماذا؟ في الواقع سوف تقوم كلمات بإعراب التعبير، لكننا لم نطلب منها أن تعطينا شجرة إعراب، لذلك سوف تعود لنا بالقيمة لاشيء. هيا نغير ذلك الموقف! فلنبدأ بشيء بسيط كالأرقام:
رقم = خانة:أ رقم:ب => أ + ب 
أو خانة 

خانة = من "0" إلى "9"

لاحظ شيئين:
في السطر الأول قلنا خانة:أ رقم:ب ، هذه طريقتنا في إعطاء أسماء للمكونات التي نعربها.
أننا قلنا في أخر السطر => أ + ب ، وهذه طريقتنا في أن نعود بقيم أثناء الإعراب

يمكن أن نقرأ السطر كله كالآتي: الرقم مكون من خانة هي أ، ورقم أصغر هو ب، وحين نجدهما نعود بالقيمة أ + ب.

لاحظ أن أ و ب هما نصان، فلو أعربنا الرقم "123" فسيكون أ = "1" و ب يساوي "23" (الأرقام تقرأ من اليسار) وبهذا يكون الرقم كله هو أ + ب = "123"

ماذا عن السطر الثاني الذي يقول أو خانة؟ نحن لم نخبره بأي قيمة يعود، لكن لو رأى مفسر كلمات قاعدة ما هي الا استدعاء قاعدة أخرى (نحن لم نفعل سوى استدعاء "خانة") فسيعود بنفس القيمة الآتية من القاعدة المستدعاه. كأني بالضبط قلت أو خانة:أ => أ

ماذا عن السطر خانة = من "0" إلى "9"؟ حين تكون القاعدة ما هي إلا قيمة نصية فالقيمة العائدة منها هي نفس القيمة النصية.

إذاً قد عرفنا ماذا يرجع من رقم. ماذا عن شيء مثل تعبير؟

تعبير = حد:أ "+" تعبير:ب => ["+"، أ، ب]
أو حد:أ "-" تعبير:ب => ["-"، أ، ب]
أو حد:أ => أ

باللغة البشرية:
التعبير هو حد اسمه أ، ثم علامة "+"، ثم تعبير أصغر اسمه ب ، وفي تلك الحالة ارجع بالمصفوفة ["+"، أ، ب]
أو هو حد اسمه أ، ثم علامة "-"، ثم تعبير أصغر اسمه ب، وهنا ارجع بالمصفوفة ["-"، أ، ب]
أو هو مجرد حد اسمه أ، وفي تلك الحالة ارجع بذلك الحد.

سهل، أليس كذلك؟ بنفس الطريقة نعدل "حد" و "أس" و"أولي" ليكون لدينا القواعد كلها:

قواعد تعبير :
    تعبير = حد : أ "+" تعبير : ب => ["+"، أ، ب]
    أو حد : أ "-" تعبير : ب => ["-"، أ، ب]
    أو حد : أ => أ 

    حد = أس:أ "×" حد:ب => ["×"، أ، ب]
    أو أس:أ "÷" حد:ب => ["÷"، أ، ب]
    أو أس:أ => أ 

    أس = عامل:أ "^" أس:ب => ["^"، أ، ب]
    أو عامل 

    عامل = رقم:أ => كعدد (أ)
    أو "س" 
    أو "(" تعبير : أ ")" => أ 

    رقم = خانة : أ رقم : ب => أ + ب 
    أو خانة 

    خانة = من "0" إلى "9" 
نهاية 

لاحظ أننا في هذا الجزء التالي قد حولنا الرقم من قيمة نصية إلى قيمة عددية:

عامل = رقم:أ => كعدد (أ) 
 
قد يبدو الأمر صعباً، لكنه أسهل بكثير من كتابة برنامج يأخذ أي صيغة رياضية ويفهمها بطريقة يدوية. القواعد كلها أخذت 30 سطراً لكنها وفرت عشرات أو مئات السطور المطلوبة لعمل مثل هذا البرنامج في لغات أخرى.

سوف تصنع لنا كلمات دالة اسمها "تعبير" (لأننا قد سمينا القواعد "تعبير")، وهذه الدالة إن أعطيناها نصاً فستعطينا شجرة إعراب. جرب أن تضيف هذا السطر في آخر البرنامج:
اطبع تعبير("12+13×14")

سوف يظهر المخرج التالي:

["+"، 12، ["×"، 13، 14]]


في الواقع الكود السابقة ليست دقيقة لأن العمليات الرياضية + - ÷ × بها خاصية left associativity. أي أن تعبير مثل 1-2-5 المفروض أن يكون (1-2)-5، لكن برنامجنا يعتبره 1-(2-5)، أي أنه يعود بالشجرة ["-"، 1، ["-"، 2، 5] بدلاً من الشجرة الصحيحة وهي ["-"، ["-"، 1، 2]، 5]

سبب المشكلة هو أنه كان المفروض في القواعد أن نقول:
تعبير = تعبير + حد
ولكننا قلنا
تعبير = حد + تعبير

للأسف لا يمكن استخدام الطريقة الصحيحة في هذا الإصدار من كلمات بسبب مشكلة اسمها left recursion (هناك طرق لحل تلك المشكلة لكنها لم تطبق في كلمات بعد). لذلك علينا الحذر: البرنامج سيعطي نتائجاً خاطئة في المدخلات أ-ب-ج أو أ÷ب÷ج، ولكن يمكننا استخدام الأقواس في تلك الحالات.

على العموم قد أنهينا جزء إعراب الصيغ الرياضية، ومازال لدينا 70 سطراً أيضاً!

الاثنين، 3 ديسمبر 2012

البوست عبارة عن كود

هذا مثال ورقة بارنسلي، من فرع من الرياضيات يسمى chaos theory، من المدهش حقاً الشكل المعقد الذي يمكن أن يظهر من هذه الكود البسيطة.

لتنفذ هذا البرنامج:
  1. حمل لغة كلمات من http://www.kalimat-lang.com
  2. قم بنسخ ولصق الكود بالكامل في محرر كلمات
  3. قم بتنفيذ البرنامج
س مشترك 
ص مشترك 
س = 0 
ص = 0 

علامة البداية 

ارسم.نقطة (س × 30 + 50، ص × 30 + 50)، 5 
الاختيار = عشوائي (100)
إذا الاختيار = 0 :
    تحويل1 ()
وإلا إذا الاختيار < 86 :
    تحويل2 ()
وإلا إذا الاختيار < 93 :
    تحويل3 ()
وإلا :
    تحويل4 ()
تم 
اذهب إلى البداية 

إجراء تحويل1 ():
    س = 0 
    ص = 0.16 × ص 
نهاية 

إجراء تحويل2 ():
    س = 0.85 × س + 0.04 × ص 
    ص = - 0.04 × س + 0.85 × ص + 1.6 
نهاية 


إجراء تحويل3 ():
    س = 0.2 × س - 0.26 × ص 
    ص = 0.23 × س + 0.22 × ص + 1.6 
نهاية 


إجراء تحويل4 ():
    س = - 0.15 × س + 0.28 × ص 
    ص = 0.26 × س + 0.24 × ص + 0.44 
نهاية

الخميس، 29 نوفمبر 2012

هيا نستخدم تعبيرات لامدا(1): الترتيب والمقارنة



 هب أن لدينا هذا الإجراء الذي يرتب مصفوفة من الأعداد (الإجراء بطيء لكنه بسيط بما يكفي للأهداف التعليمية):


إجراء رتب (م):
    لكل أ من 1 إلى عدد (م):
        لكل ب من أ + 1 إلى عدد (م):
            إذا م [أ] > م [ب]:
                مؤقت = م [أ]
                م [أ]= م [ب]
                م [ب]= مؤقت 
            تم 
        تابع 
    تابع 
نهاية 

م = [5، 12، 8، 4، 3، 9]
رتب (م)
اطبع م

هذا الإجراء سيقوم بالمهمة المطلوبة، لكن من الصعب استخدامه في تطبيقات كثيرة: لا يمكن تطبيقه إلا على مصفوفة من الأعداد أو النصوص (لأنه يستخدم المعامل أكبر من)، ولا يمكن استخدامه إلا للترتيب التصاعدي.

الإجراء يأخذ عاملاً parameter هو المصفوفة 'م'، ماذا لو كان يأخذ عاملاً آخر هو الدالة التي يستخدمها في سطر المقارنة؟
إجراء رتب (م، دالة.المقارنة):
    لكل أ من 1 إلى عدد (م):
        لكل ب من أ + 1 إلى عدد (م):
            إذا دالة.المقارنة : تنفيذها (م [أ]، م [ب]):
                مؤقت = م [أ]
                م [أ]= م [ب]
                م [ب]= مؤقت 
            تم 
        تابع 
    تابع 
نهاية 

م = [5، 12، 8، 4، 3، 9]
رتب (م، λ س، ص : س > ص)
اطبع م

لقد قمنا بثلاث تغييرات صغيرة:
  1. أضفنا عاملاً هو دالة.المقارنة
  2. بدلاً من الاختبار م[أ] > م[ب]، جعلناه دالة.المقارنة: تنفيذها(م[أ]، م[ب])
  3. عند استدعاء الإجراء رتب، أعطيناه دالة مقارنة هي λ س، ص : س > ص، أي الدالة التي لو أخذت س و ص تعود بالقيمة س>ص (القيمة 'صحيح' أو 'خطأ' حسب نتيجة المقارنة).
 هذا البرنامج سيتصرف بالضبط مثل البرنامج السابق، لكن يمكننا أن نغيره كما نشاء باستخدام العامل الجديد! مثلاً من أجل الترتيب التنازلي، نقدم دالة مقارنة بالعلامة معكوسة λ س، ص : س < ص  .
ماذا لو أردنا ترتيب مصفوفة من الأشخاص حسب السن مثلاً؟ (اعتبر لدينا فصيلة اسمها "شخص" بها بيان اسمه "سن"). وقتها يكفي أن نقول هذا:

رتب (الأشخاص، λ أ، ب : سن أ > سن ب)


ماذا لو أردنا ترتيب الأشخاص حسب السن تنازلياً؟ يكفي تغيير العلامة في تعبير لامدا.

السبت، 24 نوفمبر 2012

تعبيرات لامدا في كلمات

تم إطلاق نسخة نوفمبر 2012 من لغة كلمات، وفيها أنواع جديدة من التعبيرات: الإجراء كذا، الدالة كذا. مثلاً لو قلت:

إجراء ترحيب( ):
    اطبع "مرحباً بك" 
نهاية 

دالة مجموع (أ ، ب):
    ارجع ب: أ + ب 
نهاية 

س = الإجراء ترحيب 
ص = الدالة مجموع

سوف تجد في المتغير س كائناً object يسمح لي باستدعاء الإجراء 'ترحيب' متى أردت. وكذلك ص يحمل وسيلة لاستدعاء الدالة 'مجموع' استدعيهما هكذا:
س : تنفيذها ( ) 
ب = ص : تنفيذها (5، 10)

السطر الأول يطبع الترحيب على الشاشة، والسطر الثاني يضع القيمة 15 في المتغير ب.

لماذا قلنا "س: تنفيذها( ) " في أول سطر بينما قلنا "ب = ص: تنفيذها(...)" في الثاني؟ لأن س تعبر عن إجراء بينما ص تعبر عن دالة.

الآن ما فائدة هذا الاسلوب في إيجاد قيم تعبر عن الإجراءات والدوال؟ هيا نضرب مثلاً ظريفاً:

دالة كلهم (المصفوفة ، الاختبار) :
    لكل أ من 1 إلى عدد (المصفوفة) :
        إذا ليس الاختبار : تنفيذها (المصفوفة [أ]) :
            ارجع ب: خطأ 
        تم 
    تابع 
    ارجع ب: صحيح 
نهاية 

دالة زوجي (ر) :
    ارجع ب: باقي.قسمة (ر، 2) = 0 
نهاية 

م = [2 ، 8 ، 6 ، 12] 

اطبع كلهم (م، الدالة زوجي)

ماذا فعلنا؟ الدالة كلهم تأخذ مصفوفة ودالة اخرى اختبارية (تعود بالقيمة صحيح أو خطأ)، وتقوم بتطبيق الدالة على كل قيمة في المصفوفة، بحيث لو عادت تلك الدالة بـصحيح لكل القيم، تعود الدالة الأم أيضاً بـصحيح، وإلا عادت بـخطأ.

والآن لم تعد تحتاج كتابة هذا الـ loop مرة أخرى للتأكد أن شرطاً ما ينطبق على كل القيم في مصفوفة!

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

لحظة...سطر واحد؟ ألم أضطر لكتابة الدالة زوجي في المثال السابق؟ هذا يجعل الموضوع يأخذ أكثر من سطر واحد للأسف :(

حسناً، في الواقع لم أكن أحتاج لذلك، لأن كلمات بها أيضاً تعبيرات لامدا (lambda expressions)، وهي طريقة لكي أعرف دالة بلا اسم في المكان الذي أريد فيه قيمة تعبر عن دالة.

أي أنه بدلا من "اطبع كلهم(م، الدالة زوجي)" كان يمكن أن أقول:

اطبع كلهم (م، λ أ : باقي.قسمة (أ، 2) = 0)

هذه هي صيغة الـ Lambda expression :

λ متغيرات:  تعبير ينتج منه قيمة

ومعناها "الدالة التي تأخذ هذه المتغيرات كعوامل وترجع بقيمة هذا التعبير". مثلاً λ س: س+1 هي الدالة التي لو أخذت س تعود بـ س+1.

(لكي تكتب علامة لامدا في محرر كلمات اضغط زر Ctrl + L، لو كنت تستخدم محرراً آخر يمكنك إدخال رمز ^ بدلاً من لامدا)

في مثال "كلهم" قد مررت للدالة "كلهم" دالة أخرى، هي "الدالة التي لو أخذت أ تعود بالقيمة باقي.قسمة(أ،2)=0"، وهو نفس الدور الذي تقوم به الدالة زوجي -- التي لم نعد نحتاج إليها.

هل تريد أن تتأكد أن المصفوفة م لا تحتوي صفراً؟ بسيطة:

اطبع كلهم.ليسوا ( م ، λ أ : أ = 0 )

نحن هكذا نحاول أن ندخل بكلمات إلى عصر جديد: عصر الـfunctional programming.

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

الدوال التي تأخذ دوالاً أخرى كعوامل تسمى higher order functions. مثال آخر لمثل هذه الدوال هو الدالة تناظر:

م = [ 1 ، 2 ، 3 ، 4 ] 
اطبع تناظر ( م ، λ أ : أ × 2 )

هذه الكود سوف تطبع على الشاشة [2، 4، 6، 8] لأن تناظر تأخذ مصفوفة ودالة، وتعود بمصفوفة أخرى مليئة بقيم المصفوفة الأولى بعد تطبيق الدالة على كل منها. أي أن كل قيمة في المصفوفة القديمة لها قيمة مناظرة لها في المصفوفة الجديدة.

مثال آخر هو الدالة اختزال :
م = [ 1 ، 2 ، 3 ، 4 ] 
اطبع اختزال ( م ، λ أ ، ب : أ + ب )

الكود السابقة سوف تطبع على الشاشة القيمة 10، لأن اختزال تأخذ مصفوفة ودالة، ثم تقوم بطبيق الدالة "بين العناصر" كأني في المثال السابق حسبت 1+2+3+4. طبعاً كان يمكنني أن أقدم دالة ضرب بدلاً من الجمع لأحسب حاصل ضرب الأعداد في المصفوفة) أو دالة max لأحسب أكبر قيمة في المصفوفة..أو...أو...

الأسم "تناظر" هو ما أطلقته - في محاولة للتعريب - على الدالة المعروفة بإسم map، والدالة "اختزال" هي reduce.

ربما تكون قد سمعت عن MapReduce. إنها تكنولوجياً مبني عليها كثير من التطبيقات في شركات مثل:
  • Google
  •  Yahoo
  • Microsoft
  • Facebook
    ...وغيرهم. الـfunctional programming ليس كلاماً رياضياً نظرياً. إنه مهم للتكنولوجيا والتطوير.

    هل تريد أن نبني جوجل خاصة بنا في مجتمعنا؟ لغة كلمات بها البذور العلمية لذلك لغرسها في الأطفال.

    الأحد، 18 نوفمبر 2012

    عن كون كلمات لغة للأطفال، ولماذا لا تكون لغة احترافية

    هناك أكثر من شخص تحدث معي عن سبب إصراري أن تكون لغة كلمات لغة تعليمية وليست لغة لعمل برامج احترافية مثلها مثل Java, Python, ...الخ.

    هذا المقال سأقسمه إلى ثلاثة أجزاء:
    • الأسباب اللي تدعو لـ"حرفنة" كلمات.
    • ردي على بعض هذه الأسباب
    • متطلبات تحويل كلمات إلى لغة احترافية، لكي يكون الحوار واقعياً ويفهم القاريء الموضوع بالضبط.
    الرأي الآخر: لماذا لا تجعلها احترافية؟

    من الأسباب التي تدعو لجعل كلمات لغة احترافية:

    1. قد يكون هناك عائقاً نفسياً يمنع البعض من استخدام لغة للأطفال أو المبتدئين.
    2. ماذا سيفعل الأطفال (أو الكبار المبتدئين) بعد تعلم كلمات إن أرادوا عمل برامج احترافية؟
    3. ماذا عن حلم البرمجة باللغة العربية في كل مكان؟

    1- لن يحب أحد أن يستخدم لغة للمبتدئين: هذا جانب اجتماعي/تسويقي. هناك فئة من المبرمجين لا تحب إلا ما هو "قوي" و"احترافي". وهذا قد يدفع البعض لعدم استخدام كلمات لأنها "لعبة". هناك جانب آخر أجتماعي هو أن بعض العرب للأسف لا يرى أنه هناك منتج جيد يمكن أن يخرج من عقل مبرمج عربي، وبعض من هؤلاء سيظنون أنني جعلت كلمات للأطفال لأخفي عيوبها أو ليكون لدي عذر جاهز لأي ثغرة في اللغة: أنها للاطفال.

    2- ماذا سيفعل الأطفال (أو الكبار المبتدئين) بعد تعلمها؟ هذا سؤال معقول. هب أن شخصاً تعلم كلمات ثم أراد أن يصنع برامجاً كبيرة أو تجارية، هل سيجب عليه أن يتعلم لغة برمجة أجنبية؟ إن كنت أقول أنني صنعت كلمات لرفع حاجز اللغة الذي يعوق تعلم البرمجة، فلماذا لا أرفع أيضاً (أو بتعبير أدق: أساعد في رفع) حاجز اللغة الذي يعوق البرمجة الاحترافية؟

    3- ماذا عن تحقيق حلم البرمجة باللغة العربية؟ هذا جانب اجتماعي أيضاً: لو ظهرت لغة برمجة عربية احترافية، ألن يؤدي هذا لشعور بالفخر في سائر أنحاء الوطن العربي، وأن يزداد العرب ثقة في قدرتهم على النهوض بأمتهم، وأن يكون شيئاً جميلاً على العموم؟

    الرأي الخاص بي: لماذا لم أجعلها كذلك

    بالنسبة لجانب أن البعض سيراها لعبة: لقد تعلمت البرمجة على كمبيوتر صخر وأنا طفل، وكنت أبرمج لأستمتع بوقتي قبل أن أعرف بوجود كلية اسمها "حاسبات" أو وظيفة اسمها "مطور برامج". كلمات بدأت لأقدم نفس الفرصة لأطفال اليوم، ولم تصنع لتكون لغة احترافية.

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

    وهذا ما لا أستطيع أن أقوم به حالياً. ما أستطيع أن أقوم به هو تطوير اللغة نفسها، وهو ليس أمراً سهلاً حتى للغة مبتدئين.

    ربما هذا حلم يحتاج لمساهمة من المجتمع كله. فليصنع الناس لغات برمجة عربية أخرى، تعليمية واحترافية وتجريبية. كلمات - لمن لا يعرف - مفتوحة المصدر: يمكن لأي مبرمج أن يدرس الكود ويعرف كيف كتبت. يمكن للمجتمع إن أراد أن يتعلم منها ويصنع مثلها أو أفضل.

    الجزء الأخير: الخطوات المطلوبة لتكون كلمات احترافية

    (الحديث هنا تكنولوجي وليس اجتماعي، لن أتحدث عن التسويق أو الدعم الفني...الخ ولكن عن تطوير اللغة نفسها).

    أولاً: كلمات في صورتها الحالية لغة قوية جداً. فيها مثلاً هذه الإمكانيات:
    • tail call elimination
    • destructuring
    • lambda expressions - في الإصدارة القادمة
    •  green threads
    • CSP channels
    وهي إمكانيات معظمها لا يوجد في ++C، ولا بايثون، ولا جافا. (في حالات معينة مثل CSP  يمكن تطعيم تلك اللغات بمكتبات خارجية لتقديم هذه الإمكانيات، لكن في حالات مثل tail calls لا يمكن).

    لكن على الجانب الآخر، اللغات الأخرى تقدم تنفيذا سريعاً للبرامج، تقدم garbage collection متقدم عن كلمات (ماعدا ++C التي لا تحب مثل هذه الرفاهيات)، تقدم مكتبات لأي شيء تريده، تقدم ضماناً معقولاً لخلو المترجم والآلة الافتراضية من الأخطاء، وتقدم خاصية استدعاء دوال خارجية [بايثون تقدم ctypes لاستدعاء إجراءات مكتوبة بالسي، جافا تقدم JNI لنفس السبب، سي شارب تقدم P/Invoke، وهكذا).

    كلمات تحتاج إذاً، لكي تكون أقرب للاحترافية:
    • تطوير الآلة الافتراضية الخاصة بها (وهي من تصميمي واسمها SmallVM) لتكون أسرع وبجامع مهملات أفضل. أو كتابة نسخة من كلمات تعمل على ألة افتراضية موجودة.
    • ضبط إمكانية FFI الخاصة بها (وهي الخاصية التي تكافيء JNI/ctypes المذكورة بأعلاه). الخاصية موجودة بالفعل في كلمات لكنها تحتاج لاستكمالها.
    • إصلاح الكثير من الثغرات والنواقص الموجودة
    • صنع خاصية multiplexing over threads، ولو تم صنع هذه الخاصية فستكون ميزة نادرة لكلمات، لا توجد إلا في لغات مثل Google Go أو Erlang
    • صنع بعض المكتبات الأساسية مثل web, database, networking
    • عمل كل هذا بدون التأثير على سهولة تعلم اللغة، أو ملاءمتها للأطفال، أو جمالها [ربما يتطلب هذا فصل اللغة إلى لغتين واحدة للاطفال والأخرى احترافية، لكن بنفس الـsyntax تقريباً ونفس المكتبات].
    بعض هذه الإمكانيات يجري العمل فيه فعلاً (مثلاً أعمل حالياً في خاصية multiplexing)، والبعض مؤجل للمدى الطويل، والبعض لا أدري إن كنت سأقوم به فعلاً أم لا. من يعلم؟ هؤلاء الذين يطلبون لغة احترافية، ربما يحصلون عليها ذات يوم.