Mastering Programming

Dịch từ bài viết “Mastering Programming” của tác giả Kent Beck.

Qua nhiều năm quan sát những lập trình viên bậc thầy, tôi đã nhận thấy những mô thức chung trong quy trình làm việc của họ. Qua nhiều năm huấn luyện những lập trình viên ở mức thạo nghề, tôi đã nhận thấy sự thiếu vắng những mô thức đó. Tôi đã thấy sự khác biệt (mà) khởi đầu cho những mô thức đó, có thể tạo ra.

Đây là những cách thức mà những lập trình thu được hiệu quả tốt nhất có thể từ hơn 3 tỉ giây quý báu của họ ở trên trái đất này (ý nói là thời gian trong 1 năm – lời người dịch).
Bài luận này sẽ làm bạn sáng tỏ hơn về những điều đó. Một “công nhân thạo nghề” học cách giải quyết những vấn đề lớn hơn bằng cách cùng một lúc giải quyết nhiều vấn đề hơn. Một bậc thầy học cách giải quyết những vấn đề còn lớn hơn thế bằng cách giải quyết ít vấn đề hơn cùng một lúc. Các phần của lời khuyên này sẽ được chia nhỏ sao cho việc tích hợp những giải pháp riêng biệt sẽ là một vấn đề nhỏ hơn việc chỉ giải quyết chúng đồng thời.

Thời gian

  • Phân chia. Lấy một dự án lớn, cắt nó thành các phần nhỏ, và sắp xếp lại các phần sao cho phù hợp với hoàn cảnh. Tôi luôn có thể phân chia các dự án nhỏ hơn và luôn tìm thấy những cách thay đổi trật tự của các phần mà phù hợp với các nhu cầu khác nhau.
  • Một việc tại một thời điểm. Chúng ta tập trung vào tính hiệu quả đến mức chúng ta giảm số lượng số vòng phản hồi (feedback cycles) trong một nỗ lực để giảm tổng chi phí. Điều này dẫn đến những tình trạng gỡ lỗi phức tạp mà chi phí của nó có thể còn lớn hơn tổng phí về số vòng phản hồi mà chúng ta đã tránh.
  • Viết chương trình chạy được, chạy đúng, và chạy nhanh (Make it run, make it right, make it fast). (Ví dụ của một việc tại một thời điểm, phân chia công việc, và dễ dàng sửa đổi).
  • Những sửa đổi dễ dàng. Khi bạn phải đối mặt với một sự thay đổi phức tạp, đầu tiên hãy làm cho nó dễ đi (tôi cảnh báo rằng điều này có khó), sau đó thực hiện việc sửa đổi dễ (ví dụ: phân chia công việc, làm một việc tại một thời điểm, tập trung, và sự cô lập. Đây là một ví dụ của việc phân chia công việc.
  • Sự tập trung. Nếu bạn cần thay đổi một vài thành phần, đầu tiên hãy sắp xếp lại mã chương trình sao cho sự thay đổi chỉ cần xảy ra trong một thành phần.
  • Sự cô lập. Nếu bạn chỉ cần thay đổi một phần của một thành tố, hãy trích xuất thành tố đó ra để thay đổi trên toàn bộ thành tố con đó.
  • Quản lý các hệ thống cơ sở. Bắt đầu các dự án bằng việc so sánh với những hệ thống đã có. Điều này có thể chống lại thiên hướng muốn bắt đầu sửa chữa mọi thứ của chúng ta, nhưng khi bạn xem xét các hệ thống cơ sở, bạn sẽ thực sự biết liệu có đúng là bạn đang làm công việc sửa chữa những thứ hiện có không.

Học tập

  • Trước khi thực thi chương trình, dự đoán chính xác điều gì sẽ xảy ra.
  • Các giả thuyết cụ thể. Khi chương trình hoạt động không đúng, hãy minh định những gì bạn cho là sai trong chương trình trước khi thực hiện sự thay đổi. Nếu bạn có hai hay nhiều hơn các giả thuyết, hãy phân tích sự khác biệt giữa các giả thuyết đó.
  • Loại bỏ chi tiết không liên quan. Khi báo cáo một lỗi, hãy tìm những bước ngắn nhất để sinh lại lỗi đó. Khi cô lập một lỗi, hãy tìm những ví dụ (test case) ngắn nhất. Khi sử dụng một API mới, bắt đầu từ những ví dụ đơn giản nhất. “Tất cả những thứ này không thành vấn đề” là một giả thuyết quá mạnh và có khi là sai lầm.
    • Ví dụ, hãy xem một lỗi trên mobile, sinh lại nó với chương trình curl.
  • Đa quy mô. Di chuyển tự do giữa các quy mô (ví dụ khi thực thi trên dữ liệu nhỏ sang thực thi trên dữ liệu lớn — cách hiểu của người dịch). Có thể đây là một vấn đề thiết kế, không phải là vấn đề kiểm thử. Có thể vấn đề này liên quan đến con người chứ không phải là một vấn đề công nghệ [Lừa đảo, điều này luôn đúng].

Những thứ vượt trên cả logic

  • Tính đối xứng. Những thứ gần như nhau có thể phân chia chia thành những phần giống hệt nhau và những phần khác biệt rõ ràng.
  • Tính thẩm mỹ.
  • Nhịp điệu. Chờ đợi cho đến thời điểm đúng bảo toàn năng lượng và tránh sự lộn xộn.
  • Cân bằng (tradeoffs). Tất cả những quyết định là chủ đề của việc tradeoff. Quan trọng hơn là biết quyết định phụ thuộc vào cái gì hơn là biết câu trả lời bạn lựa chọn hôm nay (hoặc câu trả lời này bạn đã lựa chọn hôm qua).
  • Danh sách niềm vui (Fun list). Khi các ý tưởng không thứ tự đến, hãy ghi chép chúng và quay trở lại làm việc nhanh chóng. Xem lại danh sách khi bạn tạm dừng.
  • Chăm sóc các ý tưởng. Các ý tưởng giống như là những chú chim nhỏ dễ sợ hại. Nếu bạn doạ chúng đi, chúng sẽ dừng việc đến với bạn. Khi bạn có một ý tưởng, hãy chăm chút nó một chút. Kiểm chứng để loại bỏ chúng nhanh nhất có thể, nhưng từ dữ liệu chứ không phải là từ sự thiếu sự say mê.
  • Nguyên tắc 80/15/5. Sử dụng 80% thời gian của bạn cho những công việc nguy cơ thấp hoặc những công việc được trả lương vừa phải. Sử dụng 15% thời gian của bạn cho các việc nguy cơ cao/trả lương cao có liên quan. Sử dụng 5% thời gian của bạn cho những việc làm cho bạn thích thú, không quan tâm tới kết quả. Hãy dạy những người tiếp nối bạn làm 80% công việc của bạn. Vào thời gian ai đó sẵn sàng để đảm nhận công việc, một trong số 15% thí nghiệm của bạn (hoặc ít thường xuyên hơn, một trong số 5% thí nghiệm) sẽ cho kết quả và trở thành phần 80% của bạn. Lặp lại điều đó.

Kết luận

Dòng chảy trong trong bản phác thảo này dường như bắt đầu từ việc giảm nguy cơ bằng việc quản lý thời gian đến việc chấp nhận mạo hiểm một cách có ý thức bằng cách sử dụng toàn bộ não bộ và nhanh chóng quyết định độ ưu tiên của các ý tưởng.

Advertisements

Để trở thành một lập trình viên giỏi (Trích từ Quora) – Phần 2

Nguồn: câu trả lời của “Lionell Pack” (http://www.quora.com/What-are-the-best-kept-secrets-of-great-programmers)

There are a few pieces of advice I’d give. I’ll put a list at the end.

1. Pick some great developers in your field, and learn about the cutting-edge things they’re doing. Go to user groups when they’re presenting, read their blogs, read their books, help out on their open-source projects. Try to understand the principles they’re aiming at, and don’t just blindly follow the techniques. I’ve seen IoC DI implementations go horribly awry because the developer didn’t see the warning signs of the very things that pattern is supposed to avoid.

2. Avoid technique attachment (“Ooh, shiny!” syndrome). There’s a tendency to always try to use the newest techniques you’re learned. Event sourcing is cool, but it’s not the solution to every problem. IoC makes sense, most of the time. Using lambda expressions and closures can often make a solution clearer and simpler, but you don’t have to shoe-horn them into every single algorithm you write. There’s nothing wrong with starting a hobby or throw-away project with the intention of learning a new technique, but when you’re building code to solve a real-world problem, pick the right tool for the job.

3. Know your libraries and frameworks, and use them instead of writing your own implementations. Especially, don’t try to write your own date and time routines!

4. Pay attention to warnings, whether they’re coming from your compiler, IDE, or other tools. The people who write them understand the language deeply, and these warnings are their way of teaching you important details about the language. Don’t just change the code to avoid the warning; make sure you understand what the person who designed that warning is trying to tell you.

5. When you’re building code, constantly re-factor, and don’t get too attached to a design or block of code. This point deserves fleshing out a little, so I’m going to go into some more detail:

  • Be prepared to throw away code. Sometimes, you’ll write 20 lines, re-factor them a bit, and realize that you basically just re-implemented something offered by a framework or library you’re using, or something you’ve already written. Throw it out! DRY doesn’t just refer to your own code. It also covers DRYF (“Don’t Repeat Your Framework”) and DRYL (“Don’t Repeat Your Libraries”).
  • No matter how carefully you consider your up-front design (if you prefer exploratory coding, this is even more crucial), you are going to run into things you hadn’t thought of – and there’s a good chance your design won’t quite accommodate every single detail you encounter. Aggressively refactor your own code as you’re writing it (know your IDE, particularly the refactoring tools it provides). Move bits of code between classes. Introduce new classes. Split methods into two, or three, or more. Throw out classes which don’t make sense. Rename things the first time you read the name and think, “what does that mean again?”. Keep SRP in mind, and the moment you find yourself trying to make one class do two different things, turn it into two classes.
  • Writing software isn’t like running a race – you don’t start at the beginning, follow a pre-defined route, and end up at the finish. Think of it more as writing out post-it notes and shuffling them around on a board, sometimes making edits or throwing some notes out entirely, sometimes writing new ones, until you satisfy an on-looker (your tests!) that everything looks right.
  • (And finally, on that note… write automated tests. I don’t buy in to the principles of TDD, but I definitely buy into the practice. I usually flip it around in my head – it’s not that your code is complete once it satisfies the test; rather, your tests are complete once they demonstrate that the code functions correctly. I’ve seen too much code ‘proven’ by incorrect or incomplete tests to be happy with “write the test, write the minimum code to pass the test”.)

Here’s the promised list:
1. Follow good developers, and try to understand their ideas.
2. Pick the right tool, not your favourite tool.
3. Know your libraries and frameworks, so you don’t repeat them.
4. Pay attention to compiler/editor/etc. warnings!
5. Refactor all the time – especially new code, especially as you’re writing.

Để trở thành một lập trình viên giỏi (Trích từ Quora) – Phần 1

Nguồn: câu trả lời của “Jeff Darcy” (http://www.quora.com/What-are-the-best-kept-secrets-of-great-programmers)

OK, seriously this time.  I think there are really a few things that distinguish great programmers.

  1. Know the concepts. Solving a problem via memory or pattern recognition is much faster than solving it by reason alone. If you’ve solved a similar problem before, you’ll be able to recall that solution intuitively. Failing that, if you at least keep up with current research and projects related to your own you’ll have a much better idea where to turn for inspiration. Solving a problem “automatically” might seem like magic to others, but it’s really an application of “practice practice practice” as Miguel Paraz suggests.
  2. Know the tools. This is not an end in itself, but a way to maintain “flow” while programming. Every time you have to think about how to make your editor or version-control system or debugger do what you want, it bumps you out of your higher-level thought process. These “micro-interruptions” are small, but they add up quickly. People who learn their tools, practice using their tools, and automate things that the tools can’t do by themselves can easily be several times as productive as those who do none of those things.
  3. Manage time. Again it comes back to flow. If you want to write code, write code. If you want to review a bunch of patches, review a bunch of patches. If you want to brainstorm on new algorithms . . . you get the idea. Don’t try to do all three together, and certainly don’t interrupt yourself with email or IRC or Twitter or Quora.  😉  Get your mind set to do one thing, then do that thing for a good block of time before you switch to doing something else.
  4. Prioritize. This is the area where I constantly see people fail. Every problem worth tackling has many facets. Often, solving one part of the problem will make solving the others easier. Therefore, getting the order right really matters.  I’m afraid there’s no simple answer for how to recognize that order, but as you gain more experience within a problem domain – practice again – you’ll develop a set of heuristics that will guide you.
  5. Reuse everything. Reuse ideas. Reuse code. Every time you turn a new problem into a problem you already know how to solve – and computing is full of such opportunities – you can save time. Don’t worry if the transformed solution isn’t absolutely perfect for the current problem. You can refine later if you really need to, and most often you’ll find that you’re better off moving on to the next problem.

A lot of these really come down to efficiency.  As you move through more problems per day, you’ll gain more experience per day, which will let you move through more problems per day, and so on.  It’s a feedback loop; once you get on its good side, your effectiveness (and value) will increase drastically.

Cấu trúc dữ liệu Tree trong C++

Một bộ thư viện cài đặt cấu trúc dữ liệu Tree trong C++ theo STL Style khá hay và dễ sử dụng:

http://tree.phi-sci.com/

Hỗ trợ các thao tác cơ bản với Tree như chèn, xóa node trong Tree, duyệt cây theo thứ tự pre, in-order, post-order,…

Rất đáng để tìm hiểu.

Duyệt các file trong một thư mục với C++ sử dụng thư viện boost

Khi lập trình C++ thao tác với file/thư mục; một yêu cầu thường gặp là duyệt tất cả các file trong một thư mục. Các hàm xử lý thư mục không phải là hàm chuẩn trong C++, và thư viện STL cũng không có các hàm này. Trên các platform khác nhau lại có các hàm xử lý khác nhau. Điều này gây khó khăn khi chuyển từ platform này sang platform kia, ta lại phải làm lại.

Bài viết ghi chép lại cách sử dụng bộ thư viện boost cho C++ để thực hiện việc duyệt các file trong một thư viện (tổng hợp từ nhiều nguồn trên NET).

boost C++ là bộ thư viện mã nguồn mở, extend các chức năng của bộ thư viện sẵn có của C++, cho phép khả năng khả chuyển trên nhiều platform. Để sử dụng boost, trước tiên cần cài bộ thư viện này. Download tại: http://www.boost.org/

Tham khảo các tài liệu về bộ thư viện này tại: http://en.highscore.de/cpp/boost/http://shoddykid.blogspot.com/2008/07/getting-started-with-boost.html

Để duyệt các file trong một thư mục, tôi sử dụng lớp Boost Filesystem và các hàm trong lớp đó.

Đoạn chương trình sau đây minh họa cách sử dụng lớp Filesystem để duyệt các file trong một thư mục.


#include <iostream>
#include <boost/filesystem/operations.hpp>
#include <boost/filesystem/fstream.hpp>

using namespace std;

/**
* List all files in the directory
* @param dirname
*/
void directoryListing(const string& dirname) {
  // Tạo full path cho thư mục dirname
  boost::filesystem::path dirpath(dirname, boost::filesystem::native);

  // Kiểm tra xem thư mục có tồn tại
  if (!boost::filesystem::exists(dirpath)) {
    cerr << "Directory: " << dirname << " does not exist!" << endl;
    return;
  }
  if (!boost::filesystem::is_directory(dirpath)) {
    cerr << dirpath.string() << " is not a directory!" << endl;
    return;
  }

  // Khai báo iterator để duyệt thư mục
  boost::filesystem::directory_iterator end_itr;

  for (boost::filesystem::directory_iterator itr(dirpath); itr != end_itr; itr++) {
    string filename = itr->leaf(); // Tên file hiện tại
    cout << filename; //
    if (boost::filesystem::is_directory(*itr)) {
      cout << " (dir)"; // Nếu là thư mục in ra (dir)
    }
    cout << "\n";
  }
}

Khi duyệt file, ngoài việc in ra tên file/thư mục, có thể chèn các dòng code xử lý file tùy ý. Chú ý khi dịch bạn cần chỉ rõ đường dẫn tới các header files và thư viện boost (dùng tùy chọn -I và -L khi biên dịch).

[Tutorial] Xử lý file XML với libxml2 trong C

Bài viết này hướng dẫn cách xử lý file XML trong C/C++ với libxml2.

Bài viết dựa trên LibXML tutorial download tại: http://xmlsoft.org/tutorial/xmltutorial.pdf

Mục tiêu:

+ Hướng dẫn các thao tác cơ bản trong xử lý XML: như kiểm tra hợp lệ của file XML, lấy các thông attibute của các node, lấy text content của các node, duyệt các node trong XML Tree với XPath

+ Cung cấp các tài liệu tham khảo cần thiết khi xử lý file XML với C/C++

+ Cung cấp các hàm tiện ích cho xử lý file XML

(1) Định dạng XML: Dùng để lưu trữ và biểu diễn dữ liệu, đặc biệt quan trọng với các dữ liệu trên Web.

Nếu chưa quen thuộc với XML, có thể tham khảo thêm tại: http://www.w3schools.com/xml/default.asp

Ví dụ file XML:

<?xml version="1.0"?>
<note>
 <to>Tove</to>
 <from>Jani</from>
 <heading>Reminder</heading>
 <body>Don't forget me this weekend!</body>
</note>

(2) Xử lý file XML với libxml

3 kiểu dữ liệu cơ bản:

+ xmlChar: Kiểu dữ liệu cơ bản, lưu trữ 1 bye trong UTF-8 Encoding
+ xmlDoc: Cấu trúc dữ liệu chứa tree của xml doc được parsed. xmlDocPtr để lưu trữ con trỏ tới xmlDoc
+ xmlNode và xmlNodePtr: Lưu trữ 1 sing node trong cây

Function 1: Parse một xml file, trả về con trỏ xmlDocPtr, nếu văn bản không được parse thành công trả về NULL.

Thêm các dòng #include sau vào đầu các file

#include <string>
#include <libxml/parser.h>
#include <libxml/xpath.h>
#include <libxml/xmlmemory.h>

// Return a pointer of type xmlDocPtr point to xmlDoc
// @docname: xml document name
xmlDocPtr getDoc( const char* docname ) {
  xmlDocPtr doc;
  doc = xmlParseFile( docname );
  if ( doc == NULL ) {
    cerr << "Document not parsed successfully" << endl;
    return NULL;
  }
  return doc;
}

Function 2: Tìm một node với tên cho trước, trả về con trỏ xmlNodePtr

// Return a pointer to xmlNodePtr
// Find the child node with name node_name
xmlNodePtr getChildNode( xmlNodePtr parent, string node_name ) {
  xmlNodePtr cur = parent->xmlChildrenNode;
  while ( cur != NULL ) {
    if ( !xmlStrcmp( cur->name, ( xmlChar* ) node_name.c_str() ) ) {
      return cur;
    }
    cur = cur->next;
  }
  return NULL;
}

Function 3: Tìm node anh em của một node, với tên node được cho trước (Sử dụng hàm next())

// Get sibling node with @node_name
xmlNodePtr getSiblingNode( xmlNodePtr a_node, string node_name ) {
  xmlNodePtr cur = a_node->next;
  while ( cur != NULL ) {
    if ( !xmlStrcmp( cur->name, ( xmlChar* ) node_name.c_str() ) ) {
      return cur;
    }
    cur = cur->next;
  }
    return NULL;
}

Function 4: Trả về giá trị của một attribute của một node, tên cho trước

// Get attribute value of @att_name
string getAttrStr( xmlNodePtr cur, string att_name ) {
 xmlChar* uri = xmlGetProp( cur, (xmlChar*) att_name.c_str() );
 if ( uri == NULL ) {
 cerr << "Attribute " << att_name << " does not exist" << endl;
 return "";
 }
 string att_val = (char*) uri;
 xmlFree(uri);
 return att_val;
}

Function 5: Lấy text content của một node

string getElementContent( xmlDocPtr doc, xmlNodePtr cur ) {
 xmlChar * txt_content = xmlNodeListGetString( doc, cur->xmlChildrenNode, 1);
 if ( txt_content == NULL ) {
 cerr << "Text content does not exist!!!" << endl;
 return "";
 }
 string strContent = (char*) txt_content;
 trimSpaces( strContent );
 xmlFree( txt_content );
 return strContent;
}

Function 6: Trả về một tập các node theo xPath

// Return a pointer to xmlPathObject
// @doc: context of the type xmlDocPtr
// @xpath: criteria for the xpath to retrieve set of nodes
xmlXPathObjectPtr getNodeSet(xmlDocPtr doc, xmlChar* xpath) {
 xmlXPathContextPtr context;
 xmlXPathObjectPtr result;
 context = xmlXPathNewContext( doc );
 if ( context == NULL ) {
 cerr << "Error in xmlXPathNewContext" << endl;
 return NULL;
 }

 result = xmlXPathEvalExpression( xpath, context );
 xmlXPathFreeContext( context );

 if ( result == NULL ) {
 cerr << "Error in xmlXPathEvalExpression" << endl;
 return NULL;
 }

 if ( xmlXPathNodeSetIsEmpty ( result->nodesetval ) ) {
 xmlXPathFreeObject( result );
 // cerr << "No result" << endl;
 return NULL;
 }
 return result;
}

Các ngôn ngữ lập trình được dùng nhiều trong các dự án NLP

Qua kinh nghiệm sử dụng tool và lập trình của bản thân, có thể thấy các ngôn ngữ sau được sử dụng nhiều trong các dự án NLP:

(1) Java

Có lẽ do bộ thư viện của Java rất phong phú, hỗ trợ nhiều thứ, lại có khả năng chạy trên nhiều platform khác nhau mà không cần phải viết lại code nên Java được dùng khá nhiều trong các dự án NLP

Nhóm NLP của đại học Stanford dùng Java để phát triển các thư viện xử lý ngôn ngữ tự nhiên: http://nlp.stanford.edu/software.  Tất cả các software ở đây đều được viết bằng Java.

(2) C++

C++ có đặc điểm là hiệu quả, thậm chí là với dữ liệu rất lớn. Còn nhược điểm là bộ thư viện sẵn có chưa phong phú (ngay cả STL). Một số tool điển hình: MaxEnt, CRF++, Brown Clustering,…

(3) Các ngôn ngữ script: Python, Perl, Ruby. Được dùng nhiều hơn cả là Perl, và gần đây là Python với bộ NLTK khá tốt. Mình thì thích Ruby vì tính trong sáng của ngôn ngữ này.

(4) Shell script: Vẫn được dùng khá nhiều như là file batch để run tự động các chương trình theo chế độ batch