顯示具有 程式設計教學 標籤的文章。 顯示所有文章
顯示具有 程式設計教學 標籤的文章。 顯示所有文章

2010年12月7日 星期二

My Blog: QuickSort C Code





C++ code colored by C++2HTML





void testQuickSort()
{

static int a[8]={4,7,8,5,2,6,3,1};

    quickSort(a,0,7);

for(int i=0;i<8;i++)

        printf("%d",a[i]);

}
static void quickSort (int a[], int lo, int hi)
{

//  lo is the lower index, hi is the upper index
//  of the region of array a that is to be sorted
    int i=lo, j=hi, h;

    int x=a[(lo+hi)/2];

    //  partition

    do
    {    
        while (a[i]<x) i++; 
        while (a[j]>x) j--;

        if (i<=j)
        {
            h=a[i]; a[i]=a[j]; a[j]=h;

            i++; j--;
        }
    } while (i<=j);

    //  recursion

    if (lo<j) quickSort(a, lo, j);

    if (i<hi) quickSort(a, i, hi);
}



2010年12月5日 星期日

3. Output Formatting

3. Output Formatting:
1. Field Width Setting field width is very simple. For each variable, simply precede it with "setw(n)". Like this:
#include 
#include iomanip

using namespace std;

int main()
{
const int max = 12;
const int width = 6;
for(int row = 1;row <= max;row++) {
for(int col = 1;col <= max;col++) {
cout << setw(width) << row * col;
}
cout << endl;
}
return 0;
}
Notice how "setw(n)" controls the field width, so each number is printed inside a field that stays the same width regardless of the width of the number itself.

2010年11月25日 星期四

C++ - how to convert string to uppercase/lowercase

C++ - how to convert string to uppercase/lowercase:
On Dec 17, 12:29 pm, Michal wrote:
> Hallo
> I looked through ANSI/ISO C++ standard string, and I did not find any
> function from string class that would do so. Did I overlooked
> something or it is so?


Here's the STL-ish way.

//////
#include
#include
#include

int my_toupper(int c)
{
return toupper(c);
}

int
main(int argc, char **argv)
{
using namespace std;
string s = "hello world";
transform(s.begin(), s.end(), s.begin(), my_toupper);
}
////

You have to create my_toupper (or use a cast) due to a
C++ wart.

Sean

2010年7月25日 星期日

Programming Contests, Software Development, and Employment Services at TopCoder

Programming Contests, Software Development, and Employment Services at TopCoder:

演算法筆記 – 最新消息


演算法比賽的競賽流程說明:http://www.topcoder.com/wiki/display/tc/Algorithm。 大致上分為三個階段:第一個階段是程式解題,自己寫好解答程式碼,自己測試過沒問題之後就可以上傳,等候批改;第二個階段是找別人程式中的漏洞,自己擬好 測試資料去測別人寫的程式,測出漏洞後自己可得到分數、別人會降低分數,但是實施測試卻測不出漏洞,自己就會被扣分;第三個階段是由系統幫大家批改程式, 並且統計分數。

2010年7月24日 星期六

Qt-interest Archive - Using DLL files

Qt-interest Archive - Using DLL files:
Hi,

I want to call a dll file within my program so that I can call the functions in it. Please advise me how can i do it. As I am very new to Qt, it will be better if you can provide a simple example as well.

Please advise.
Thanking you in advance.

Regards,
Alex


---------------------------------
Stay in the know. Pulse on the new Yahoo.com.  Check it out. 

Message 2 in thread

human being schrieb:
> Hi,
>   
>   Please advise me how can i do it. As I am very new to Qt,

This has nothing to do with Qt. Read e.g. the docs in the MSDN and the
docs about Visual Studio about creating/linking with DLLs.

In case you're using a *.pro/qmake based build system, you can use
something like win32:LIBS -ltheLib.lib as to link with 'theLib.dll'.
Read the documentation about qmake (it also tells you the switches you
have to set as to create a DLL)

--
[ signature omitted ]

Message 3 in thread

Hi,

Thanks a lot for replying!

Do you mean that I just need to change the project file and compile it. Then in my program I can call the functions of the dll file?

Forgive me if that is not what you meant, because I am very new to Qt, and this is my first time dealing with dll files. That is why I may sound stupid.

Please advise.
Many thanks...

Regards,
Alex

Till Oliver Knoll  wrote:
human being schrieb:
> Hi,
>
> Please advise me how can i do it. As I am very new to Qt,

This has nothing to do with Qt. Read e.g. the docs in the MSDN and the
docs about Visual Studio about creating/linking with DLLs.

In case you're using a *.pro/qmake based build system, you can use
something like win32:LIBS -ltheLib.lib as to link with 'theLib.dll'.
Read the documentation about qmake (it also tells you the switches you
have to set as to create a DLL)

--
[ signature omitted ]
Message 4 in thread

2010年4月26日 星期一

Why you shouldn’t use Dev-C++ « Unrefined Awesome

Why you shouldn’t use Dev-C++ « Unrefined Awesome

December 18, 2008

  1. Dev-C++ has not been updated since 2005 and is not currently maintained. Given the availability of newer software packages the majority of the programming community has moved on from Dev-C++, making it hard (sometimes impossible) to find help with any issues you may have. Bugs which are currently present in the software are unlikely to ever be fixed; according to the SourceForge listing, at the time of writing there are 340 known bugs.
  2. The “devpack” system provided for working with 3rd party libraries is no longer maintained or supported by many libraries, leading to Devpacks either being unavailable or out of date in many cases. When not working with one of these packages it can be difficult and non-obvious how to otherwise get 3rd party code working in the Dev-C++ environment.

Given the availability of free alternatives it’s amazing anyone still clings to this outdated beast, and frankly quite ridiculous that it ever ends up in the hands of anyone who is just now starting out. To finish up, what are those free alternatives?
  1. Microsoft Visual C++ 2008 Express Edition is by far the best option when it comes to Windows-based IDEs; it costs nothing, allows commercial distribution of products, and is a fully featured solution including excellent debugging facilities.
  2. Code::Blocks fills the void once occupied by Dev-C++ as an alternative solution, and can also be used on other platforms. An excellent alternative to the higher-quality MS solution.
  3. For those who really want a simpler environment, your compiler of choice in combination with Programmer’s Notepad or a similar solution is a far better choice than the outdated and unsupported Dev-C++.
  4. For those who just can’t let Dev-C++ go, the wxDev-C++ project is an updated version with less bugs and a few additional features. I’ve not personally used this and see no real reason why given the excellent options from MS or Code::Blocks, but from what I’m told it works acceptably.
小D:也就是說Dev-C++太舊了,從2005年到2008年12月這段期間就沒更新了,
而Dev-C++ 5到現在也還沒出,
我去看Dev-C++在sourceforge上的release date:

Release Date: 2005-03-17

可見到今天2010年4月26日也還沒更新!
如還不想放棄Dev-C++,建議用wxDev-C++

2010年4月5日 星期一

傳遞 function template @ novus log :: 痞客邦 PIXNET ::

傳遞 function template @ novus log :: 痞客邦 PIXNET ::
template 
void Output1(const T& obj)
{
cout << obj << endl;
}

template 
void Call(Fun f, const T& obj)
{
f(obj);
}

Call(&Output1, 12345);
Call(&Output1, "goodbye world.");

struct Output2
{
   template 
   void operator()(const T& obj)
   {
      cout <<>
   }
};

template 
void Call(Fun f, const T& obj)
{
   f("header --- ");
   f(obj);
}



Call(Output2(), 12345);

2010年1月9日 星期六

Pragma once - 維基百科,自由的百科全書

Pragma once - 維基百科,自由的百科全書

Pragma once

維基百科,自由的百科全書

跳轉到: 導航, 搜尋
CC++ 程式語言中,#pragma once 是一個非標準但是被廣泛支援的前置處理符號,會讓所在的檔案在一個單獨的編譯中被include防範一次。以此方式,#pragma once 提供類似include防範的目的,但是擁有較少的程式碼且能避免名稱的碰撞。
請參考include防範裡其中一種狀況的範例或其他的使用方法。如下:
檔案「grandfather.h」
#pragma once

struct foo {
int member;
};
檔案「father.h」
#include "grandfather.h"
檔案「child.c」
#include "grandfather.h"
#include "father.h"

[編輯] 優缺點

使用#pragma once 代替 include 防範將加快編譯速度,因為這是一種高階的機制;編譯器會自動比對檔案名稱或inode而不需要在標頭檔去判斷 #ifndef#endif
另一方面,部份編譯器,例如GCC,也包含特別的程式碼來識別和有效率的管理 include 防範。[1]
此外,因為編譯器自己必須承擔管理 #pragma once,它不必定義新的指令名稱,例如在 include防範文章範例的 H_GRANDFATHER。這能排除名稱碰撞的風險,意思就是包含一個以上相同的標頭檔不會再有錯誤
然而,這種高階的管理有好也有壞;設計者必須依賴編譯器正確的管理 #pragma once。編譯器如果犯錯,例如沒有辨認出在相同檔案中的兩個不同符號連結名稱指標,此時編譯會錯誤。編譯器對於 #pragma once 可能包含相關的臭蟲 LCC-Win32 2004年 [2][3] 和 GCC 1998年[4] 2005年,GCC 文件中將 #pragma once 列為「已淘汰」的特性。[5]

[編輯] 外部連結

Include防範 - 維基百科,自由的百科全書

Include防範 - 維基百科,自由的百科全書

Include防範

維基百科,自由的百科全書

跳轉到: 導航, 搜尋
CC++ 程式語言中,#include 防範,有時被稱作巨集防範,用於處理 #include 指令時,可避免重複引入的問題。在標頭檔加入 #include 防範是一種讓檔案等冪的方法。

目錄

[隱藏]

[編輯] 重複引入

以下的C語言程式展示了缺少 #include 防範時會出現的問題:
檔案「grandfather.h」
struct foo {
int member;
};
檔案「father.h」
#include "grandfather.h"
檔案「child.c」
#include "grandfather.h"
#include "father.h"
此處 child.c間接引入了兩份grandfather.h標頭檔中的內容。明顯可以看出, foo 結構被定義兩次,因此會造成編譯錯誤。

[編輯] 使用 #include 防範

檔案「grandfather.h」
#ifndef H_GRANDFATHER
#define H_GRANDFATHER

struct foo {
int member;
};

#endif
檔案「father.h」
#include "grandfather.h"
檔案「child.c」
#include "grandfather.h"
#include "father.h"

此處grandfather.h第一次被引入時會定義巨集H_GRANDFATHER。當child.h再次引入grandfather.h時,#ifndef測試失敗,編譯器會直接跳到#endif的部分,也避免了第二次定義foo結構。程式也就能夠正常編譯。


[編輯] 困難

為了讓#include 防範正確運作,每個防範都必須檢驗並且有條件地設定不同的前置處理巨集。因此,使用了 #include 防範的方案必須制訂一致性的命名方法,並確定這個方法不會和其他的標頭檔或任何可見的全域變數衝突。
為了解決這個問題,許多 C 和 C++ 程式開發工具提供非標準的指令 #pragma once。在標頭檔中加入這個指令,能夠保證這個檔案只會被引入一次。不過這個方法會被潛在性顯著的困難阻撓,無論 #include 指令是否在不同的地方,但實際上起源於相同的開頭(舉例,請參考 符號連結)。同樣的,因為 #pragma once 不是一個標準的指令,它的語意在不同的程式開發工具中也許會有微妙的不同。

[編輯] 外部連結

2010年1月3日 星期日

Link Options - Using the GNU Compiler Collection (GCC)

Link Options - Using the GNU Compiler Collection (GCC)

3.13 Options for Linking

These options come into play when the compiler links object files into an executable output file. They are meaningless if the compiler is not doing a link step.
object-file-name
A file name that does not end in a special recognized suffix is considered to name an object file or library. (Object files are distinguished from libraries by the linker according to the file contents.) If linking is done, these object files are used as input to the linker.
-c
-S
-E
If any of these options is used, then the linker is not run, and object file names should not be used as arguments. See Overall Options.
-llibrary
-l library
Search the library named library when linking. (The second alternative with the library as a separate argument is only for POSIX compliance and is not recommended.)
It makes a difference where in the command you write this option; the linker searches and processes libraries and object files in the order they are specified. Thus, `foo.o -lz bar.o' searches library `z' after file foo.o but before bar.o. If bar.o refers to functions in `z', those functions may not be loaded.
The linker searches a standard list of directories for the library, which is actually a file named liblibrary.a. The linker then uses this file as if it had been specified precisely by name.
The directories searched include several standard system directories plus any that you specify with -L.
Normally the files found this way are library files—archive files whose members are object files. The linker handles an archive file by scanning through it for members which define symbols that have so far been referenced but not defined. But if the file that is found is an ordinary object file, it is linked in the usual fashion. The only difference between using an -l option and specifying a file name is that -l surrounds library with `lib' and `.a' and searches several directories.
-lobjc
You need this special case of the -l option in order to link an Objective-C or Objective-C++ program.
-nostartfiles
Do not use the standard system startup files when linking. The standard system libraries are used normally, unless -nostdlib or -nodefaultlibs is used.
-nodefaultlibs
Do not use the standard system libraries when linking. Only the libraries you specify will be passed to the linker, options specifying linkage of the system libraries, such as -static-libgcc or -shared-libgcc, will be ignored. The standard startup files are used normally, unless -nostartfiles is used. The compiler may generate calls to memcmp, memset, memcpy and memmove. These entries are usually resolved by entries in libc. These entry points should be supplied through some other mechanism when this option is specified.
-nostdlib
Do not use the standard system startup files or libraries when linking. No startup files and only the libraries you specify will be passed to the linker, options specifying linkage of the system libraries, such as -static-libgcc or -shared-libgcc, will be ignored. The compiler may generate calls to memcmp, memset, memcpy and memmove. These entries are usually resolved by entries in libc. These entry points should be supplied through some other mechanism when this option is specified. One of the standard libraries bypassed by -nostdlib and -nodefaultlibs is libgcc.a, a library of internal subroutines that GCC uses to overcome shortcomings of particular machines, or special needs for some languages. (See Interfacing to GCC Output, for more discussion of libgcc.a.) In most cases, you need libgcc.a even when you want to avoid other standard libraries. In other words, when you specify -nostdlib or -nodefaultlibs you should usually specify -lgcc as well. This ensures that you have no unresolved references to internal GCC library subroutines. (For example, `__main', used to ensure C++ constructors will be called; see collect2.)
-pie
Produce a position independent executable on targets which support it. For predictable results, you must also specify the same set of options that were used to generate code (-fpie, -fPIE, or model suboptions) when you specify this option.
-rdynamic
Pass the flag -export-dynamic to the ELF linker, on targets that support it. This instructs the linker to add all symbols, not only used ones, to the dynamic symbol table. This option is needed for some uses of dlopen or to allow obtaining backtraces from within a program.
-s
Remove all symbol table and relocation information from the executable.
-static
On systems that support dynamic linking, this prevents linking with the shared libraries. On other systems, this option has no effect.
-shared
Produce a shared object which can then be linked with other objects to form an executable. Not all systems support this option. For predictable results, you must also specify the same set of options that were used to generate code (-fpic, -fPIC, or model suboptions) when you specify this option.1
-shared-libgcc
-static-libgcc
On systems that provide libgcc as a shared library, these options force the use of either the shared or static version respectively. If no shared version of libgcc was built when the compiler was configured, these options have no effect. There are several situations in which an application should use the shared libgcc instead of the static version. The most common of these is when the application wishes to throw and catch exceptions across different shared libraries. In that case, each of the libraries as well as the application itself should use the shared libgcc. Therefore, the G++ and GCJ drivers automatically add -shared-libgcc whenever you build a shared library or a main executable, because C++ and Java programs typically use exceptions, so this is the right thing to do. If, instead, you use the GCC driver to create shared libraries, you may find that they will not always be linked with the shared libgcc. If GCC finds, at its configuration time, that you have a non-GNU linker or a GNU linker that does not support option --eh-frame-hdr, it will link the shared version of libgcc into shared libraries by default. Otherwise, it will take advantage of the linker and optimize away the linking with the shared version of libgcc, linking with the static version of libgcc by default. This allows exceptions to propagate through such shared libraries, without incurring relocation costs at library load time. However, if a library or main executable is supposed to throw or catch exceptions, you must link it using the G++ or GCJ driver, as appropriate for the languages used in the program, or using the option -shared-libgcc, such that it is linked with the shared libgcc.
-static-libstdc++
When the g++ program is used to link a C++ program, it will normally automatically link against libstdc++. If libstdc++ is available as a shared library, and the -static option is not used, then this will link against the shared version of libstdc++. That is normally fine. However, it is sometimes useful to freeze the version of libstdc++ used by the program without going all the way to a fully static link. The -static-libstdc++ option directs the g++ driver to link libstdc++ statically, without necessarily linking other libraries statically.
-symbolic
Bind references to global symbols when building a shared object. Warn about any unresolved references (unless overridden by the link editor option `-Xlinker -z -Xlinker defs'). Only a few systems support this option.
-T script
Use script as the linker script. This option is supported by most systems using the GNU linker. On some targets, such as bare-board targets without an operating system, the -T option may be required when linking to avoid references to undefined symbols.
-Xlinker option
Pass option as an option to the linker. You can use this to supply system-specific linker options which GCC does not know how to recognize. If you want to pass an option that takes a separate argument, you must use -Xlinker twice, once for the option and once for the argument. For example, to pass -assert definitions, you must write `-Xlinker -assert -Xlinker definitions'. It does not work to write -Xlinker "-assert definitions", because this passes the entire string as a single argument, which is not what the linker expects. When using the GNU linker, it is usually more convenient to pass arguments to linker options using the option=value syntax than as separate arguments. For example, you can specify `-Xlinker -Map=output.map' rather than `-Xlinker -Map -Xlinker output.map'. Other linkers may not support this syntax for command-line options.
-Wl,option
Pass option as an option to the linker. If option contains commas, it is split into multiple options at the commas. You can use this syntax to pass an argument to the option. For example, `-Wl,-Map,output.map' passes `-Map output.map' to the linker. When using the GNU linker, you can also get the same effect with `-Wl,-Map=output.map'.
-u symbol
Pretend the symbol symbol is undefined, to force linking of library modules to define it. You can use -u multiple times with different symbols to force loading of additional library modules.

Footnotes

[1] On some systems, `gcc -shared' needs to build supplementary stub code for constructors to work. On multi-libbed systems, `gcc -shared' must select the correct support libraries to link against. Failing to supply the correct flags may lead to subtle defects. Supplying them in cases where they are not necessary is innocuous.

2010年1月1日 星期五

extern 和 C/C++,C++技術文章,C++系列教程,C++

extern 和 C/C++,C++技術文章,C++系列教程,C++

1、 聲明外部變量

現代編譯器一般採用按文件編譯的方式,因此在編譯時,各個文件中定義的全局變量是
互相透明的,也就是說,在編譯時,全局變量的可見域限制在文件內部。下面舉一個簡單的例子。創建一個工程,裡面含有A.cpp和B.cpp兩個簡單的C++源文件:

//A.cpp
int i;
void main()
{
}


//B.cpp
int i;


這兩個文件極為簡單,在A.cpp中我們定義了一個全局變量i,在B中我們也定義了一個全局變量i。我們對A和B分別編譯,都可以正常通過編譯,但是進行鏈接的時候,卻出現了錯誤,錯誤提示如下:
Linking...
B.obj : error LNK2005: "int i" (
?i@@3HA) already defined in A.obj
Debug/A.exe : fatal error LNK1169: one or more multiply defined symbols found
Error executing link.exe.

A.exe - 2 error(s), 0 warning(s)

這就是說,在編譯階段,各個文件中定義的全局變量相互是透明的,編譯A時覺察不到B中也定義了i,同樣,編譯B時覺察不到A中也定義了i。但是到了鏈接階 段,要將各個文件的內容「合為一體」,因此,如果某些文件中定義的全局變量名相同的話,在這個時候就會出現錯誤,也就是上面提示的重複定義的錯誤。因此, 各個文件中定義的全局變量名不可相同。
在鏈接階段,各個文件的內容(實際是編譯產生的obj文件)是被合併到一起的,因而,定義於某文件內的全局變量,在鏈接完成後,它的可見範圍被擴大到了整 個程序。這樣一來,按道理說,一個文件中定義的全局變量,可以在整個程序的任何地方被使用,舉例說,如果A文件中定義了某全局變量,那麼B文件中應可以使 用該變量。修改我們的程序,加以驗證:
//A.cpp

void main()
{
i = 100; //試圖使用B中定義的全局變量
}
//B.cpp
int i;


編譯結果如下:
Compiling...
A.cpp
C:\Documents and Settings\wangjian\桌面\try extern\A.cpp(5) : error C2065: 'i' : undeclared identifier
Error executing cl.exe.

A.obj - 1 error(s), 0 warning(s)

編譯錯誤。
其實出現這個錯誤是意料之中的,因為:文件中定義的全局變量的可見性擴展到整個程序是在鏈接完成之後,而在編譯階段,他們的可見性仍侷限於各自的文件。編 譯器的目光不夠長遠,編譯器沒有能夠意識到,某個變量符號雖然不是本文件定義的,但是它可能是在其它的文件中定義的。雖然編譯器不夠遠見,但是我們可以給 它提示,幫助它來解決上面出現的問題。這就是extern的作用了。
extern的原理很簡單,就是告訴編譯器:「你現在編譯的文件中,有一個標識符雖然沒有在本文件中定義,但是它是在別的文件中定義的全局變量,你要放行!」我們為上面的錯誤程序加上extern關鍵字:
//A.cpp

extern int i;
void main()
{
i = 100; //試圖使用B中定義的全局變量
}


//B.cpp
int i;


順利通過編譯,鏈接。

2、 在C++文件中調用C方式編譯的函數

C方式編譯和C++方式編譯
相對於C,C++中新增了諸如重載等新特性,對於他們的編譯,必然有一些重要的區別。
我們將下面的小程序分別按C和C++方式編譯,來探討兩種編譯方式的區別。
int i;

int func(int t)
{
return 0;
}

void main()
{
}


以C方式編譯的結果:
COMM _i : DWORD

PUBLIC _func
PUBLIC _main

以C++方式編譯的結果:
PUBLIC
?i@@3HA ; i
PUBLIC ?func@@YAHH@Z ; func
PUBLIC _main

可見,C方式編譯下,變量名和函數名之前被統一加上了一個下劃線,而C++編譯後的結果卻複雜的多,i變成了?i@@3HA,func變成了?func@@YAHH@Z。C++中的這種看似複雜的命名規則是為C++中的函數重載,參數檢查等特性服務的。

多文件程序中的函數調用一般情況下,工程中的文件都是CPP文件(以及頭文件)。如下面的程序僅包含兩個文件:A.CPP和B.CPP:
//A.CPP
void func();

void main()
{
func();
}


//B.CPP
void func()
{
}


程序的結構是這樣的:在文件B.CPP中定義了一個函數void func(),main函數位於文件A.CPP,在main函數中調用了B中定義的函數func()。要在A中調用B中定義的函數,必須要加上該函數的聲 明。如本例中的void func();就是對函數func()的聲明。如果沒有聲明的話,編譯A.CPP時就會出錯。因為編譯器的目光只侷限於被編譯文件,必須通過加入函數聲明 來告訴編譯器:「某個函數是定義在其它的文件中的,你要放行!」,這一點跟用extern來聲明外部全局變量是一個道理。

需要注意的是,一般的程序都是通過包含頭文件來完成函數的聲明。拿本例來說,一般是創建一個頭文件B.H,在頭文件中加入聲明語句void func(); 並且在A.CPP中加入包含語句:#include 「B.H」。在C++程序中,頭文件的功能從函數聲明被擴展為類的定義。


不同編譯方式下的函數調用。如果在工程中,不僅有CPP文件,還有以C方式編譯的C文件,函數調用就會有一些微妙之處。我們將B.CPP改作B.C:
//A.CPP
void func();

void main()
{
func();
}


//B.C
void func()
{
}


對A.CPP和B.C分別編譯,都沒有問題,但是鏈接時出現錯誤。
Linking...
A.obj : error LNK2001: unresolved external symbol "void __cdecl func(void)" (
?func@@YAXXZ)
Debug/A.exe : fatal error LNK1120: 1 unresolved externals
Error executing link.exe.

A.exe - 2 error(s), 0 warning(s)

原因就在於不同的編譯方式產生的衝突。
對於文件A,是按照C++的方式進行編譯的,其中的func()調用被編譯成了call
?func1@@YAXXZ。如果B文件也是按照C++方式編譯的,那麼B中的func函數名也會被編譯器改成?func1@@YAXXZ,這樣的話,就沒有任何問題。
但是現在對B文件,是按照C方式編譯的,B中的func函數名被改成了_func,這樣一來,A中的call
?func1@@YAXXZ這個函數調用就沒有了著落,因為在鏈接器看來,B文件中沒有名為?func1@@YAXXZ的函數。
事實是,我們編程者知道,B文件中有A中調用的func函數的定義,只不過它是按照C方式編譯的,故它的名字被改成了_func。因而,我們需要通過某種方式告訴編譯器:「B中定義的函數func()經編譯後命名成了_func,而不是
?func1@@YAXXZ,你必須通過call _func來調用它,而不是call ?func1@@YAXXZ。」簡單的說,就是告訴編譯器,調用的func()函數是以C方式編譯的,fun();語句必須被編譯成call _func;而不是call ?func1@@YAXXZ
我們可以通過extern關鍵字,來幫助編譯器解決上面提到的問題。
對於本例,只需將A.CPP改成如下即可:
//A.CPP
extern "C"
{
void func();
}
void main()
{
func();
}


察看彙編代碼,發現此時的func();語句被編譯成了call _func。

3、 補充

同2一樣,仍然是C,C++混合編程的情形,考慮下面的程序:
//A.CPP
extern int i;

void main()
{
i = 100;
}


//B.C
int i;


程序很簡單:在文件B.C中定義了一個全局變量i,在A.CPP中使用了這個全局變量。
編譯沒有問題,鏈接時卻出現錯誤:
Linking...
A.obj : error LNK2001: unresolved external symbol "int i" (
?i@@3HA)
Debug/A.exe : fatal error LNK1120: 1 unresolved externals
Error executing link.exe.

A.exe - 2 error(s), 0 warning(s)

這是因為,在C方式編譯下,i被重命名為_i,而在C++方式下,i會被重命名為
?i@@3HA
因而,我們只用extern int i;來聲明還不夠,必須告訴編譯器,全局變量i是以C方式編譯的,它會被重命名為_i,而不是
?i@@3HA。我們修改A.CPP,如下:
//A.CPP

extern "C"
{
int i;
}
void main()
{

i = 100;
}


程序正常通過編譯和鏈接。我們察看一下彙編代碼,發現語句i = 100;被編譯成了mov DWORD PTR _i, 100。

2009年12月29日 星期二

An ellipsis at the end of the parameter

An ellipsis at the end of the parameter specifications is used to specify that a function has a variable number of parameters. The number of parameters is equal to, or greater than, the number of parameter specifications.
int f(int, ...);
C++ The comma before the ellipsis is optional. In addition, a parameter declaration is not required before the ellipsis.
C At least one parameter declaration, as well as a comma before the ellipsis, are both required in C.

C: Ellipsis operator (…) : printf « Programming in Linux

C: Ellipsis operator (…) : printf « Programming in Linux

C: Ellipsis operator (…) : printf

For more updates, check Programming Insights.
Ever imagined how printf works, even though we are able to pass a number of arguments to it. If we design a function which takes two arguments and pass three parameters, we are bound to get this error “too many arguments to function”i.e., suppose we have a function
int fun2(int a, int b)
and we call the function
fun2(2,3,4)
we are sure to get the above error. So the question is how printf / scanf works with variable number of arguments? This is because C has a feature called ellipsis (…) by which you are able to pass variable number of arguments?
So the prototype of printf is
int printf(const char *str,...)
But the next question is how then can we access the arguments in the function. This can be done by the power of pointers. Let’s take a pointer which points to the last argument before …
and depending on the next arguments of what we expect, we increment the pointer and increment it accordingly
Below is a simple code which shows how this can be done
int print(const char *str,...)

/*str has the number of integers passed*/

{

int i;

int num_count=atoi(str);

 int *num=(int *)&str;

for(i=1;i<=num_count;i++)

                printf("%d ",*(num+i));}

int print_num(int num_count,...)

/*num_count contains the number of integers passed*/

{

int i;

int *num=&num_count;

for(i=1;i<=num_count;i++)

printf("%d ",*(num+i));

}

int main()

{

print_num(3,2,3,4);

print("3",2,3,4);

}

For more updates, check Programming Insights.

2008年7月30日 星期三

Google Maps API 實作範例

Google Maps API 實作範例
2007/9/24

本篇文章介紹如何利用 Google Maps API 來把 Google 所提供的地圖服務內嵌到你的網頁中,並且將座標定至你所要標記的地點。
步驟1:先申請一組 Google Maps API Key

  1. [ 按此進入 ] Google Maps API Key申請頁面。
  2. 將核取控制項打勾,並將欲申請Google Mass API的網址填入(例如:http://my-web-design.blogspot.com/)。
  3. 按 "Generate API Key" 取得 Key。
  4. 最後會出現三個方塊,分別是你取得的 Key,你指定的 URL,以及一個範例。
    範例中會有如下的片段:
    <script src="http://maps.google.com/maps?file=api&v=2&key=ABQIAAAAsYV7UusYC8W-CUCDLpFQYRREFBcOGxpqCCUdK6ZnJW0Tb_Kj_BRWDfzZYc6lwJiQloNiuOKFThoUBQ"
    type="text/javascript"></script>
    紅字的部份就是你的 Google 地圖 key,此段JavaScript需放至<head></head>之間。

步驟2:取得欲標記的地點的經緯座標值
  1. [ 按此進入 ] Google Maps搜尋頁面。
  2. 輸入欲標記的地點的地址或名稱,並按"搜尋地圖"(例如:台南火車站)。
  3. 確認搜尋結果無誤後,請按"連結至此網頁",取得地圖的連結。
    連結如下:
    http://maps.google.com/maps?f=q&hl=zh-TW&geocode=&q=%E5%8F%B0%E5%8D%
    97%E7%81%AB%E8%BB%8A%E7%AB%99&sll=22.996733,120.212465
    &sspn=0.00721,0.014462&ie=UTF8&ll=22.997686,120.212467&spn=0.00721,0.014462
    &z=16&iwloc=addr&om=1
    紅字的部份分別是經度及緯度。

步驟3:將以下範例程式片段copy到<body></body>之間:

<!--Google Map 顯示的位置,可自由決定大小-->
<div id="mymap" style="width: 500px; height: 500px"></div>
<!--以下為控制Google Maps的JavaScript-->
<script type="text/javascript">
//<![CDATA[
var map = new GMap(document.getElementById("mymap"));
//設定要顯示的控制項
map.addControl(new GSmallMapControl());
map.addControl(new GMapTypeControl());
//決定你 Google 地圖的中心點位置和縮放大小
map.setCenter(new GLatLng(22.996733, 120.212465), 16);
//標記在 Google 地圖上的經緯度
var point = new GLatLng(22.996733, 120.212465);
var marker = new GMarker(point);
map.addOverlay(marker);
//在地圖上放置標點說明
var html = "台南火車站";
map.openInfoWindowHtml (map.getCenter(), html);
//]]>
</script>

[ 按此看範例 ]

詳細說明:

//設定要顯示的控制項
map.addControl(new GSmallMapControl());
map.addControl(new GMapTypeControl());

Google Maps API 內建四種控制項:

  1. GLargeMapControl : 適合給大型地圖的控制項。
  2. GSmallMapControl : 適合給小型地圖的控制項。
  3. GSmallZoomControl : 只有 Zoom Level 的調整,沒有地圖移動控制。
  4. GMapTypeControl : 顯示地圖型態切換的控制項。

//決定你 Google 地圖的中心點位置和縮放大小
map.setCenter(new GLatLng(22.996733, 120.212465), 16);

設定 Google 地圖的中心點位置和縮放大小:

  • setCenter(GLatLng(經度, 緯度), [+ 放大(近)] -> [- 縮小(遠)]);

//標記在 Google 地圖上的經緯度
var point = new GLatLng(22.996733, 120.212465);
var marker = new GMarker(point);
map.addOverlay(marker);
//在此標記所要顯示的資訊
var html = "台南火車站";
map.openInfoWindowHtml (map.getCenter(), html);

在地圖上標記地點:

  1. 設定座標值:
    var point = new GLatLng(22.996733, 120.212465);
    var marker = new GMarker(point);
  2. 在地圖上放置標點 :
    map.addOverlay(marker);
  3. 在地圖上放置標點說明 :
    var html = "台南火車站";
    map.openInfoWindowHtml (map.getCenter(), html);




14
2
4
1

clickcomments

ClickComments: 按圖示來表達你的想法

進一步了解 | 取得這個 Widget




正!棒!酷!


Posted by 大鼻子 星期一, 九月 24, 2007

2008年7月29日 星期二

C.K. Blog » 常用Open Source

C.K. Blog » 常用Open Source

Drupal
Drupal 是一個彈性高,模組化強的內容管理系統,不管是想建立部落格 (Blogger),網站架設,電子商務平台,都可完成。
http://tw-drupal.info/
e107
e107 是一套知名的 CMS ( 內容管理系統 ),其架構是由 MySQL 與 PHP 組成。
http://e107.org/

eXo platform
eXo platform 是一套企業入口網站系統 (Portal),適合用來架設公司網站(對外)以及個別員工的資訊平台(對內)。
http://www.exoplatform.com/
Jahia
提供完整的 Portal (又名 EIP)解決方案。
http://www.jahia.org/jahia/Jahia
Joomla
一套由 Mambo 4.5.2 版衍生出來的入口網站軟體。
http://www.joomla.org/
Liferay
Liferay Portal 是一套相當成熟的Java/J2EE portal system,遵循 Portlet API JSR168 的標準。 http://www.liferay.com/
Mambo
用 PHP 來撰寫的架站軟體。
http://www.mamboserver.com/
PHP-Nuke
使用php+MySQL建立一個入口網站
http://www.phpnuke.org/
Plone
一套以 Zope來開發的網站內容管理系統,適合以群組合作方式來建立網站內容。
http://plone.org/
Xoops
用 PHP 來撰寫的架站軟體。
http://www.xoops.org/

員工入口網站 (Employee Portal) /協同作業平台 (Collaboration Platform)

eGroupware
在同一個網站應用程式裡包含許多實用的群組軟體。適合一般辦公室團隊作內部使用,提供辦公室或專案工作小組資源分享、資訊交換的協同平台。
http://www.egroupware.org/
PHPfileNavigator
PHPfileNavigator 是一套線上檔案管理系統,可方便的將文件資訊,共享與傳送,並可支援不同的作業系統。使用簡便,是線上檔案管理系統中一個好的選擇。
http://pfn.sourceforge.net/
Timeclock
Timeclock 是一套簡單容易上手的網頁打卡鐘系統,可以輕易實現網路線上即時打卡,取代傳統打卡簽到。
http://timeclock.sourceforge.net/
Tutos
一套功能不錯的群組軟體,適合一般中小企業的工作團隊內部使用。
http://www.tutos.org/
WebCalendar
提供一個個人或群組使用的行事曆,具備如安排會議、郵件通知、分享行事曆等功能。
http://webcalendar.sourceforge.net/

專案管理系統 (Project Management)

DotProject
一套專案管理系統,讓專案管理員儲存專案產生的各種相關資料。
http://www.dotproject.net/

購物網站 (Shopping Mall)

OSCommerce
快速地建立一個含購物車、線上管理台等網上購物站。
http://www.oscommerce.com/
XTCommerce
快速地建立一個含購物車、線上管理台等網上購物站 。
http://www.xtcommerce.com/

企業資源規劃(ERP)/財務會計(Accounting)

Compiere
以 Java 來開發的 ERP 系統,深受國外歡迎。
http://www.compiere.org/
TurboCASH
TurboCASH 是一套相當簡潔的進銷存以及會計軟體,適合小型的企業使用。
http://www.turbocashuk.com/
webERP
webERP 是一套 web-base 的 ERP 系統,主要功能為財務會計的管理,以及庫存管理的應用。
http://www.weberp.org/

客戶關係管理 (CRM)

SugarCRM
以 PHP 來開發的 CRM 系統,讓工作人員透過網頁瀏覽器查尋客人相關的資料。
http://www.sugarcrm.com/
vtiger CRM
Vtiger CRM 是一套 100% 開放原始碼客戶關係系統,主要協助企業建立與客戶之間業務往來互動資訊。
http://www.vtiger.com/index.php?option=com_content&task=view

線上教學系統 (e-learning)

Atutor
提供一個支援線上教學的網站。
http://www.atutor.ca/
Moodle
一套功能強大、版面簡潔的線上教學系統。
http://www.moodle.org/

流量訪客資料分析

AWStats
AWStats 是一個免費的功能強大的服務器日誌分析工具,它可以告訴你所有的Web統計數據,包括訪問量、訪問者數量、頁面、點擊、高峰時段等等。
phpMyVisites
這是一套免費的PHP + MySQL的網站訪客分析軟體,支援多站台管理及多國語言轉換
http://www.phpmyvisites.us/
Cacti
可以取代MRTG的一套軟體,結合SNMP及RRDTool工具,並使用php+MySQL去做有效管理也可外掛 Scripts 及加上 Templates 來作出各式各樣的監控圖
http://www.cacti.net/

其他

Care2x
Care2x 主要是一套以 PHP 撰寫的醫療資訊管理系統。
http://care2x.com/
Enhydra Shark
支援標準的商業流程引擎格式及提供流程設計介面。
http://shark.objectweb.org/
Knowledge Tree
這是一套知識管理系統 (KM),幫助企業快速搜尋及版本控管各類如 Word、HTML、TXT、PDF 等文件檔案。
http://kt-dms.sourceforge.net/
Phplist
是一個管理通訊名單(mailing list)及電子快訊(Newsletter)的網上應用程式。
http://tincan.co.uk/phplist
phpMyTicket
phpMyTicket 是一套線上訂位系統,適合用來線上訂票與選位(如購買電影票或上課券)。
http://www.phpmyticket.com/
PhpMyAdmin
一套全能功的 MySQL 管理及應用程式。讓使用者透過網頁瀏覽器去檢視、建立或修改資料庫的大小事務。
http://www.phpmyadmin.net/
Webmin
一套強大的 Linux 管理工具,讓管理員能夠透過網頁瀏覽器去管理 Linux 系統及設定各種伺服器。
http://www.webmin.com/

「網頁載入中,請稍候...」的簡單作法

「網頁載入中,請稍候...」的簡單作法

隨著自由欄位的越來越五彩繽紛、五花八門之後,網頁載入的速度也越來越慢。為了提示讀者耐心等候,特別加了個「載入中」的小框,就像AJAX技術常見的樣式一般。以下說明我在 Xuite 裡達成的方法與步驟。

首先在顯示在最上面的自由欄位裡加上如下HTML標籤:

<div id="divLoading"
    style="border:3px blue solid;background-color:#ba2a4a;color:white;position:absolute;top:-50px;left:350px;width:250px;height:50px;z-order=0">
  <img src="http://b.blog.xuite.net/b/3/a/c/11893557/blog_10351/txt/11293067/7.gif"
       style="vertical-align:middle;padding:6px"/>
  網頁載入中,請稍候...
</div>

基本結構就是用一個區域(div,division) 把圖形和載入中的文字包在一起,因為是放在第一個自由欄位裡,所以會在很早的時間裡就執行到而顯示在自由欄位上方50像素的位置(top: -50px)。因為這段HTML 是放在第一個自由欄位裡,自由欄位的左上角就是座標 (0, 0),要將此區域向上移動就是把底端用負值即可,此例中我把位置向上移了50個像素(-50px)。
類似AJAX的「載入中」圖形在網路上可以找到不少,其中AJAXLoad是一個能自行設定的圖形產生網頁,可依需求指定圖形的樣式與顏色等;如果您懶得去找或用產生器的話,以下是一些我上載好的:

AjaxLoading 1
http://b.blog.xuite.net/b/3/a/c/11893557/blog_10351/txt/11293067/7.gif

Ajax Loading 2
http://b.blog.xuite.net/b/3/a/c/11893557/blog_10351/txt/11293067/0.gif

Ajax Loading 3
http://b.blog.xuite.net/b/3/a/c/11893557/blog_10351/txt/11293067/9.gif

Ajax Loading 4
http://b.blog.xuite.net/b/3/a/c/11893557/blog_10351/txt/11293067/8.gif

Ajax Loading 5
http://b.blog.xuite.net/b/3/a/c/11893557/blog_10351/txt/11293067/6.gif

最後在任何一個自由欄位裡設定window.onload事件,在此事件裡把顯示出來的區域(divLoading)的顯示樣式變成 "none",則「載入中」的框框就消失了。

<script>
  function init( ) {
    // .... 其他指令
    _oTag = document.getElementById("divLoading");
    _oTag.style.display = "none";  // hide it.
  }
  if (window.attachEvent) {
    window.attachEvent('onload', init);
  } else {
    window.addEventListener('load', init, false);
  }
</script>

2008年6月13日 星期五

蔡學鏞【言程序】部落格: 思考函數編程(三)FP is as FP does

蔡學鏞【言程序】部落格: 思考函數編程(三)FP is as FP does

到底FP有哪些常見的特色?阿甘(Forrest Gump)說過「Stupid is as stupid does」。這個句型相當好用,套用到FP,就是「FP is as FP does」。我們可以透過FP作了些什麼(does),來瞭解FP是什麼(is)。


【Higher-Order Functions】某函數如果可以接受函數當作參數,或者以函數為傳出值,我們就稱這樣的函數為「較高次方函數」(Higher-Order Function)。較高次方函數讓程式可以變得相當有彈性,而且寫法比OOP更精簡許多。函數式語言有相當大的威力來自較高次方函數,特別是函數式語言 的程式庫往往會有許多較高次方函數,可以幫助你進行資料處理(例如特殊排序法、資料對應、資料過濾)、事件處理。


【Currying】 程式庫往往將函數定義得比較一般化,具有通用性。這樣的函數,需要傳入比較多的參數。利用Currying的方式,可以定義出「特殊化」的函數,這樣的特 殊化函數將函數的局部參數先指定好,只有剩下一部份參數沒有指定。例如,程式庫有個函數為power(x, y),會計算出x的y次方,那麼我們可用很簡單的語法,定義一個平方函數square(x),事先將參數y指定為2;且定義立方函數cubic(x),事 先將參數y指定為3。當然非函數式語言也做得到這一點,但不可能像函數式語言的語法這麼精簡。


【Lazy Evaluation】表示式的執行可以拖延到真正需要執行時才執行,這就是Lazy Evaluation。好處是可以讓編譯器有最大的優化空間(敘述的次序可以先後調動)、執行時可以符合當時最新的狀況、方便做出更高階的抽象結構、定義 無窮大的資料結構。上述的這些優點,如果不透過Lazy Evaluation,都不太容易做到(甚至不可能做到)。


【Continuations】 利用Continuation,可以將一個函數的傳出值,傳進另一個函數當作傳入值,也可以產生循序執行的效果。利用continuation的方式,可 以讓原本沒有狀態的技術,有了狀態,讓應用更好寫。利用Web原本是無狀態的,如果將Continuation用到Web的開發上,會使得開發變得容易許 多。


【Pattern Matching】模式比對的方式,可以讓系統自動幫我們進行分支(branch),與變數的指定(assignment)。有了模式比對,FP可以降低 依賴(imperative語言的)switch/case與(物件導向語言的)多型,而且寫出來的程式碼也不會像switch/case那樣一大塊。


【Closure】Closure讓函數在離開之後,其context依然保留(而不會像call stack內的frame一樣,被丟棄)。有了Closure,就可以設計「傳出值是函數」的函數。


【List Processing】FP的始祖語言LISP,名稱的意思正是List Processing,目的是要進行方便的List處理(List是資料的集合)。許多函數式語言都有好用的List處理語法(例如List Comprehensions、取出List頭部元素、插入List頭部)與Lisp處理函數(例如map、filter)。


【Meta-Programming】許多FP語言都可以有提供方便的Meta-Programming工具,讓你可以設計自己的DSL,來輔助軟體開發。


並 非所有的函數式語言都具有上述的特色。許多函數編程語言都不是「純的」函數語言,而是混合了相當多的imperative特性。學習函數編程時,如果使用 這些語言,可能比較無法體會到函數編程的意義。我鼓勵大家學習比較純的函數式語言,並多多使用它們的程式庫,這可以讓你對FP有比較深刻的體驗。(全文 完)

蔡學鏞【言程序】部落格: F#:微軟的下一代重量級語言

蔡學鏞【言程序】部落格: F#:微軟的下一代重量級語言
在下一個版本的Visual Studio中,F#將會成為正式的一員,和C#、Visual Basic平起平坐。目前,F#也已經和Visual Studio做了初步的整合。
一個新語言的誕生,自然有它的時代背景,對F#來說,自然是函數式編程(FP,Functional Programming)的因素。我已經在許多文章提到FP對現在IT環境的重要性,請自行上網查詢閱讀這些文章,我不在此重複敘述。
事實上,除了具備FP的能力,F#在Imperative Programming與OOP(物件導向編程)方面的能力,也是不打折的。因此,利用F#寫程式,可以享有高度的自由,想用什麼樣的paradigm, 你可以自己作主。但如果完全不使用FP,那麼使用F#的意義不大,不如回頭使用C#。

雖然F#比其他FP語言(Haskell、Erlang、Common Lisp)似乎更好學(因為語法比較不奇怪),但是一般人要善用F#並不容易,主要是FP的Paradigm和我們慣用的OOP與Imperative Programming不同。因此,熟悉F#的過程中,一開始最好強迫自己只用FP的方式寫F#程式,不要用到OOP和Imperative Programming。

FP編程常用到的技巧包括了下面八點:Higher-Order Function、Currying、Lazy Evaluation、Continuations、Pattern Matching、Closure、List Processing、Meta-Programming。令人驚訝地,F#對這八點的支援都相當不錯。許多FP語言在這些方面可能還比不上F#完整。一 開始寫F#程式,盡量多用這些技巧,寧可矯枉過正,這是學習的必經階段。

OS: 這八點是啥?老實說,我也不清楚。

我認為,想要開始習慣用F#寫FP程式,你可以開始做下面的事:



  1. 將Object為主的程式碼轉回Procedure的方式:將Object化成Record,將Method轉成Function,將this(或self)當作Function的參數,把Function集中放到Module(模組)。

  2. 將程式中用到迴圈的地方,盡量轉成遞迴(Recursion)。先不要管執行效率的問題。

  3. 將程式中用到if/else或switch/case的地方,改用Pattern Matching(模式比對)。

只要做到上述這三件事,你的F#程式會具有濃濃的FP風味。


F#由於是建構在.NET平台上,所以當然和.NET有天衣無縫的整合。事實上,F#所提供的互動式環境,對於學習.NET API來說,是相當不錯的工具,比PowerShell更適合程式員使用。


由於F#的開發相當早(2002年),且F#比較是屬於靜態的語言,而不是動態的語言,所以目前F#並沒有以.NET的DLR(Dynamic Language Runtime)為平台。未來F#會不會搬到DLR上頭?情況還不明朗。


以往用FP開發的系統多以科學和財務為主,現在有了F#和.NET,應該可以為FP打開更廣的應用領域。我相信,漸漸地會有人改用F#寫.NET的各種應用(ASP.NET、GUI、LINQ)。


F# 也是一種Language-Oriented Programming(語言導向編程)的語言。所謂的「語言導向編程」,就是Meta-Programming,也就是「可以建立自己的DSL」。我寫 過一篇關於「GPL & DSL」的文章,但是這篇文章並未得到太多讀者的注意,顯然大家並沒有意識到Meta-Programming的重要性,相當可惜。


GPL與DSL

文 / 蔡學鏞

扔掉浴室裡的瓶瓶罐罐,GPL一瓶抵萬瓶。為什麼我們要推出GPL這麼好的產品?鞏利俯身彎腰,素珠側身插腰,齊聲說道:『因為你值得!』

看了上面這段廣告,如果你是個神智正常的人,應該會啼笑皆非。『GPL是什麼東東呀?這麼神奇!別把我當容易上當的傻B』… 但其實我們都曾經上當過。

GPL (General-Purpose Language,一般用途的語言)是指「可以用在許多開發領域,沒有特定用途」的語言。例如:C、C++、Java、C#、Ruby、Python都算 是GPL。在適合使用GPL的地方使用GPL,當然沒問題;但是在不適合GPL的地方使用它,事情可能一樣可以做得到,只是要花更多精力,事倍功半,且潛 在的問題會更多。

在特定的專業領域(例如GUI、資料庫、Web、統計)使用GPL,不見得很適合。如果在專業領域能改用專為該領域打造 的SPL(Special-Purpose Languag,特殊用途語言),程式會變得相當簡短,且表達力更豐富、生產力更高、也更不容易有bug。許多人也將SPL稱為DSL(Domain- Specific Language),或小語言(little language)。

對於DSL,我們其實並不陌生。SQL就是一種DSL,讓我們和資料庫管理系統(DBMS)進行溝通。SQL有自己的語法,SQL只能用來進行資料庫相關的動作,不能用來寫一般的程式,這就是相當典型的DSL。

除了SQL之外,「小語言」在UNIX上有長遠的歷史,為UNIX環境增添許多吸引力,例如Awk就是UNIX使用者愛用的小語言。近幾年引起大家關注的RoR(Ruby-on-Rails)也是一種DSL。RoR讓你不需要寫很多程式碼,就可以做出網站。

最 近許多XML應用,也都是DSL的表現,例如微軟的XAML、Adobe的MXML。這些XML都是將GPL的物件模型直接對應到XML,並未發揮太多 DSL的優點。而且由於採用「不適合人類編寫」的XML格式,因此最好搭配特別的設計工具(例如GUI設計工具)。我認為,這類XML格式的DSL,只是 一種便宜行事的作法,廠商應該要想辦法設計出更好的DSL。

如何設計出DSL?不同的GPL對此有不同的作法,例如REBOL、Ruby、Common Lisp、Curl、PowerShell、Groovy都號稱讓你可以設計自己的DSL,但是它們對於DSL的支援程度,以及DSL的語法彈性,卻大大地不同。

如 果某GPL允許方便地自訂DSL,我們說這種語言支援meta-programming。即使一個語言沒有支援meta-programming,它依然 可以做出DSL,只是 (1) 會相當辛苦,要自己寫剖析器(parser),或者 (2) 做出來的DSL,彈性相當差。例如:在C++/MFC時代,有所謂的事件發派巨集(event dispatch macro),我也認為它是一種DSL,只是囿於C/C++的巨集威力有限,所以此「事件發派巨集」的彈性相當糟糕。

不要因此對巨集留下不好的印象。許多語言(包括Curl和Common Lisp)的巨集具有很強的DSL功能,可不像C/C++的巨集這麼陽春。

除 了巨集之外,也常見使用外部的語言和工具,來設計DSL。例如ANTLR讓我們用文字描述方式,定義自己的DSL,它會幫我們產生Parser。或者,你 也可以使用微軟推出的DSL工具,以繪圖的方式,描述DSL的語法。外部定義DSL的作法,或許可以產生不同GPL的剖析器。

如果DSL和GPL的資料和程式完全不能流通,那就沒什麼意思了!畢竟開發應用時,我們必須以GPL為主,特殊的地方才用DSL,兩者之間最好沒有隔閡。

所以,某GPL對於DSL的支援良窳,我的三個基本的判斷原則是: (1) 能否輕易地設計DSL (2) 能否設計出有彈性的DSL (3) 能否讓DSL和GPL使用上合為一體。在未來的編程環境,具有良好了DSL支援,絕對是一大加分。

除 了文章一開始的萬用洗髮精之外,瑞士刀也適合用來比喻GPL。瑞士刀有很多功能,但是我想除了馬蓋先之外,我們還是喜歡用各種不同的刀子,去做不同的事。 職場講究專業分工,不同才能的員工各司其所。語言也應該專業分工,別再讓通才語言做專才語言的工作,這才是未來軟體開發該走的路。

2008年6月2日 星期一

猴在當下: Euler 計畫和其他

猴在當下: Euler 計畫和其他
Project Euler 蒐集了一系列的挑戰問題,這些問題的答案都是一個數字或者一組數字。每當你破解一個問題後,你就能與其他同樣破解這個問題的人,討論答案與心得。通常解決這問題需要一點程式設計技巧和數學知識。
網站裡面也會對於不同國家或者程式語言的解題者做統計和排名。比方說這是台灣的統計,而這是 Python 語言的統計。和「點點點」相比,台灣的表現很爛。
一般所謂的 online judge 系統其實很多,ACM 風格的 online judge 是主流。 UVa online judge 是其中的代表。其他還有如ZeroJudge
和 Project Euler 不同, Project Euler 上傳的是答案,而這類 online judge 要你上傳的是「程式」。所以有限定使用的程式語言。標準的語言是 C,C++,Java。雖然我也寫 C++和C,但我還是比較喜歡能自由選擇工具。這點到不是根本性的問題,像是 Sphere Online Judge 就能讓你使用許多不同的程式語言,包含 perl, ruby, python。
另 外一個不同點是時間限制,雖然 Project Euler 也有所謂的「一分鐘法則」,就是程式應該要能在一分鐘跑完,但這不是硬性規定,真正的裁判是你自己。事實上,如果你喜歡(而且可以的話),你甚至可以 用google 找答案。真正的裁判是你自己。我喜歡這種風格。
另外一種類型是 Code Golf,著重在用最短短的程式碼達成指定的功能,就像是打高爾夫一樣,你想要用最少的桿數完成比賽。雖然也是程式解題競賽,但又另有一番風格。
當然,寫程式的目的在於寫出「真正有用」的東西,而不是解答這些問題來炫耀自己有多強。
(對 Project Euler 有興趣的朋友,可能也會對 IBM 的 Ponder This 有興趣。)

小P:呵呵,我有在玩UVA online judge,解了二十多題,只有一題抄人,一題有些部份用別人的code,
其它全部純手工哦。

2008年5月31日 星期六

蔡學鏞【言程序】部落格: 思考函數編程(二)Why FP

蔡學鏞【言程序】部落格: 思考函數編程(二)Why FP:
儘管各種語言有差異,但是大致上來說,FP的共同點在於:「沒有副作用」(Side Effect)、「第一級函數」(First-Class Function)。「沒有副作用」是指在表示式(expression)內不可以造成值的改變;「第一級函數」是指函數被當作一般值對待,而不是次級公 民,也就是說,函數可當作「傳入參數」或「傳出結果」。

基本上,遵守上述兩點進行程式編寫,差不多就可以稱為FP。

遞迴可以保存狀態,可以讓程式變得相當精簡,但是成本(時間與記憶體)也很高。所以,許多時候,函數式語言會希望我們將程式寫成尾端遞迴(Tail Recursion),以便編譯器自動將它編譯成記憶體的直接跳躍(也就是迴圈)。

為了提昇效率,許多函數式語言會納入imperative的某些作法(例如允許副作用),這類的FPL被稱為不純(Impure)的函數式編程語言,例如 Ocaml、F#、LISP、REBOL。當然也有一些語言堅持Pure Functional的作法,例如Erlang、Haskell、Occam、Oz。主要是以Erlang為首的純函數式語言,似乎更能充分展現出FP的優勢。

這使得單元測試相當容易,只要管引數的結果正確與否就好,不需要管函數呼叫的次序正確與否,或者外部狀態是否做好正確的設定。如果是像C、Java或C#這類語言,檢查函數的傳出值是不夠的,因為函數執行過程中可能會改變外部狀態。但是對於FP來說,就不用擔心這一點。

想除錯,就必須能讓此錯誤可以重現(reproduce),然後定位(locate)錯誤的地方。對FP來說,由於沒有外部狀態的因素干擾,所以上述這兩 點都相當容易就可以做到。Erlang的某個函數只要會出錯,就一定每次都會出錯,所以可以「重現」;C語言的某個函數出錯,卻不見得每次都出錯,相當麻 煩。一旦知道某個函數出錯,你可以快速地在Erlang函數內找出問題所在,而予以修正;但是對C語言來說,外部狀態影響太多,不容易除錯。

FP 相當適合寫(concurrency)的程式。沒有共享記憶體,沒有執行緒,不需要擔心critical section,不必使用mutex等上鎖機制。由於沒有外部狀態的問題,FP的程式也相當適合進行程式碼「熱抽換」或「熱部署」(Hot Code Deployment) -- 你可以不需要關閉你的軟體系統,可以直接部署新的程式模組。

小P:FP相當適合寫concurrency的程式,在目前朝向多核心架構下,怪不得FP又熱了起來。