C#, C++에 이어서.. Infinite Streams를 이번에는 Python으로 구현 해보았습니다.
 최근에 사내에서 Python으로 프로젝트를 하나 진행하고 있는데..
 Python도 참 매력적인 언어라는 생각이 듭니다.

 Python에는 언어자체에서 infinite를 지원하고 있습니다.
 따라서 이전에 C++로 삽질을 한 것에 비하면 아래 처럼 매우 쉽게 구현할 수 있습니다.

from itertools import *

def fibs():
    yield 1;
    yield 1;
    h, t = tee(fibs())
    t.next()
    for i in imap(lambda (a,b):a+b, izip(h, t)):
            yield i

for i in islice(ifilter(lambda x:x%2==0, fibs()), 10): print i
 C# 코드와 사실상 별 다를바가 없는 코드지만... 언어에서 지원해주는 것이 많은 만큼 코드가 약간 깔끔해졌다(?)는 느낌이 드네요 .

Posted by U_Seung


이전에 C#으로 SICP에 나온 Infinite Streams을 구현 해본적이 있는데
이번에는 C++로 구현 해보았습니다. 실제로 이런 Lazy한 기법들이 사용될 곳이 있을지는 생각을 더 해봐야 겠지만 일단은 공부 한다는 차원에서 .... 해볼만한 것 같습니다.


만든 Infinite를 사용하는 main코드는 아래와 같은 모양으로 생겼습니다.
(wcout를 쓰기 위해서 그냥 tchar를 사용하지 않았습니다.)

bool isEven(int i) { return (i%2 == 0); }
 
int wmain(int argc, wchar_t* argv[])
{
    fibs()->filter(isEven)->take(10)->out(std::wcout);
    return 0;
}

눈치가 빠르신 분들은 벌써 아셨겠지만 코드는...
피보나치수열(fibs)를 얻어서, 이 중 짝수만 걸러내서(filter) 그 중에서 제일 앞에 10개만(take) 출력(out)하겠다는 코드 입니다. 물론 C++에서 저렇게 exception처리도 없이 ->연산자를 중복해서 사용하는 것은 좋지 않지만 실제로 사용하는 코드가 아니니 이해해 주시기 바랍니다.

아무튼 그래서 피보나치 수열 중에서 짝수 중 앞에 10개는 아래와 같습니다.
위 코드가 아래와 같은 결과를 나오게 만들면 되겠지요?
시간 있으신 분들은 제가 한 방법 말고 다른 방법으로 구현 해보셔도 재미있을 것 같네요 ^^

2
8
34
144
610
2584
10946
46368
196418
832040


- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
소스보기 :




- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

소스를 직접 까보기 귀찮은신 분들을 위해서..
주요 부분 소스를 붙입니다.. (메모리는.. shared_ptr을 썼다가 지저분해서 뺐습니다.)

class fibo_stream : public make_stream<int>
{
public:
    fibo_stream()
        : make_stream<int>(1, 1)
    {}
    virtual IStream<int>* _tail()
    {
        IStream<int>* super_tail = make_stream<int>::_tail();
        return new fibo_stream(super_tail->head(),
                                head()+super_tail->head());
    }
protected:
    fibo_stream(int head, int tail)
        : make_stream<int>(head, tail)
    {}
};
IStream<int>* fibs() { return new fibo_stream(); }

피보나치 수열 스트림의 포인트는 항상 두개의 값만 계산해 놓는다는 겁니다. 즉, 미리 앞서나가지 않는 것 입니다.


덧)
아래 wafe님의 지적을 받아서 표준이 아닌 문법을 고쳐서 g++에서도 컴파일 될 수 있도록 했습니다.


Posted by U_Seung



이번에는 Element Object에서 어떻게 this가 활용되는지 알아 보겠습니다.
지난번 글에서 this가 Object 내부의 함수에서 사용(evaluation)될 때, this가 해당 Object를 참조한다는 것을 알아 보았습니다.

마찬가지로, Element Object내의 함수에서 this가 사용되면 this는 해당 Element를 참조하게 됩니다. HTML+JS 를 통해서 이를 살펴보도록 하겠습니다.


#1. HTML에서의 Event 등록과 JS에서의 Event 등록의 차이

<input id="button" type="button" />

function eventButtonClick() {
     element = document.getElementById("button");
    (this == window).print("this == window");
    (this == element).print("this == element");
}


document.getElementById("button").onclick = eventButtonClick;


(결과)
> this == window: false
> this == element: true


위와 같이 Script에서 onClick에 해당 함수를 넣을 경우에 해당 Button을 클릭하면, this가 해당 element 나오는 것을 알 수 있습니다.

<input id="button" type="button" onclick="eventButtonClick()" />


function eventButtonClick() {
   
element = document.getElementById("button");

   
(this == window).print("this == window");

   
(this == element).print("this == element");

}
(결과)
> this == window: true

> this == element: false


반면에.. HTML에서 해당 Event에 함수를 지정한 경우에는
해당 Button을 클릭할 때, this는 window가 나오는 것을 알 수 있습니다.




#2. 차이가 발생하는 이유

도대체 왜 이런 차이가 생기는 것일까요?
일단 Click 이벤트가 발생이 되면..
document.getElementById("button").onclick();
와 같은 방식으로 해당 이벤트 핸들러가 호출 됩니다.

이때 함수 내부에 어떠한 차이가 있는지 각각의 경우에 함수 내부의 모습을 출력 해보도록 하겠습니다.

<input id="button" type="button" onclick="eventButtonClick()" />

function eventButtonClick() {
   (this == window).print("this == window");
}

document.getElementById("button").onclick.print("HTML");
document.getElementById("button").onclick = eventButtonClick;
document.getElementById("button").onclick.print("Script");

(결과)
> HTML: function anonymous() { eventButtonClick(); }
> Script: function eventButtonClick() { (this == window).print("this == element"); }


Script에서 이벤트 메소드에 함수를 지정하면, 해당 함수 Object가 직접 할당 시키는 효과를 가지지만, HTML에서 함수를 지정하면 해당 부분이 String으로 되어 있기 때문에 함수 Object가 직접 할당 되지 않고, anonymous 함수 내부에서 evaluation 되는 형태로 지정되게 됩니다.

즉, Script에서 이벤트 등록을 위해서 실행 되는 코드는...

element.onclick = function() { alert(this); }

와 같다고 한다면. HTML에서 이벤트 등록을 위해서 실행되는 코드는..

var func = function() { alert(this); }
element.onclick = function() { func(); }

와 같이 된다고 할 수 있겠습니다.
엄연한 차이가 발생합니다. 지난번 글을 유심히 보셨으면 위 둘 사이의 차이가 쉽게 보이실 것 같습니다.





#3. HTML에서도 Element Object의 this를 사용하기

그렇다면 HTML에서 Event를 지정하면서 this가 현재 Object를 참고하고 싶게 한다면 어떻게 해야 할까요?

<input id="button" type="button" onclick="eventButtonClick(this)" />

function eventButtonClick(sender) {
    element = document.getElementById("button");
    (sender == element).print("sender == element");
    (sender == window).print("sender == window");
}

(결과)
> sender == element: true
> sender == window: false
 

위 처럼 HTML에서 this를 적어 주면 됩니다.
이는 마치 위의 예제 형태로 단순화 시켜서 나타내면..

var func = function(sender) { alert(sender); }
element.onclick = function() { func(this); }

위와 같은 형식이 되겠습니다.
왠지 적고나니 지난번 글의 중복인것 같아서..
아래에 재미난(?) 퀴즈를 하나... ;;


##. 보너스 퀴즈

<input id="button" type="button" value="Element Object" />


var
value = "Window Object";

var obj = function() {

    this.value = "Prototype Object";

    this.func = function() { this.value.print("Name"); };

    };

obj.value = "My Object";

obj.func = function() { this.value.print("Name"); }

document.getElementById("button").onclick = (빈칸)


위 코드에 보면 각 Object에 따라서 value값에
(A) Element Object
(B) Window Object
(C) Prototype Object
(D) My Object
의 네 개의 값이 들어 있습니다.

button을 눌렀을 때, (A)~(D)의 String이 출력되도록 할려면 빈칸에 어떤 코드가 들어가야 할까요?

Posted by U_Seung


 이번에는 Javascript의 기본 문법에 있는 this, new, prototype이 어떻게 Scope에 영향을 주는지 알아보겠습니다.


#1. This는 Evalute되는 순간이 중요

각각의 경우에 aa값이 어떻게 출력될 지 예상 해보세요.

var aa = 10;

function check1() { this.aa.print("check1"); };

var obj = {

    thisref: this,

    aa: 20,

    check1: check1,

    check2: function() { this.aa.print("obj.check2"); },

    innerObj: {

        thisref: this,

        aa: 30,

        check1: check1,

        check3: function() { this.aa.print("obj.innerObj.check3"); }

        }

    };

 

check1();

obj.check2();

obj.check1();

obj.innerObj.check3();

obj.innerObj.check1();

(obj.thisref == window).print("obj.thisref == window");

> check1: 10
> obj.check2: 20
> check1: 20
> obj.innerObj.check3: 30
> check1: 30
> obj.thisref == window: true

 this는 Object내의 Method로 된 함수 내에서 현재 Object를 참조하는 역할을 합니다.
this는 함수가 어디서 선언 되었느냐 보다는 함수가 어느 Object에서 Evaluation 되었느냐가 중요하며, Evaluation되는 순간의 현재 Object를 참조 합니다.

 아무런 한정자가 없는 함수나 변수는 가장 최상위 Object인 window Object의 하위 Node에 속하게 됩니다. 따라서 check1()으로 실행될 때는 마치 window.check1()으로 호출되는 것과 같은 효과를 가지게 되며 this.aa는 window.aa가 되어서 10의 값을 가집니다.
 obj.check2()와 obj.innerObj.check3() 경우의 this는 현재 자신의 Object를 참조 합니다.
 obj.check1()과 obj.innerObj.check1()의 경우가 약간 혼동을 주는 케이스 입니다. 분명 이 함수들은 동일한 Context를 가지는 check1을 호출하게 되지만 Evalutation되는 순간 참조하는 Object가 각각 다르기 때문에 다른 this값을 가지게 됩니다. ( 만약 this.aa가 아닌 aa라면 check1을 호출하는 세 함수는 같은 값(10)을 가집니다. )
 경우에 따라서는 임의로 참조하는 Object를 변경하고 싶을 때가 있습니다. 이럴 때는 call()이나 apply()를 사용하면 됩니다.


#2. new는 Function의 prototype으로 Object를 만든다

Javascript에서는 Class를 만드는데 prototypenew를 제공합니다. new operator를 이용하면, Function type의 변수를 가지고 Object를 생성할 수 있습니다. 이때, 생성되는 Object는 Function Object의 prototype property의 값을 기본값으로 가지고 있습니다.

var aa = 10;

var check = function() { this.aa.print("check()"); };

var func = function() { this.check(); }

func.aa = 20;

func.check = function () { this.aa.print("func.check()"); };

func.prototype.aa = 30;

func.prototype.check = function () {
                          this.aa.print("func.prototype.check()"); };

 

func();

func.aa.print("func.aa");

func.check();

var newFunc = new func();

newFunc.aa.print("(new func).aa");

newFunc.check();

> check(): 10
> func.aa: 20
> func.check(): 20
> func.prototype.check(): 30
> (new func).aa: 30
> func.prototype.check(): 30

헷갈리지 말아야 할 부분은 new operator는 prototype 부분을 기반으로 하여서 Object를 만들어 준다는 것입니다. Function type도 Object type에 기반하기 때문에 다른 값들을 추가할 수 있는데 이는 new 로 생성된 object와는 아무런 연관이 없습니다.



#3. 참조가 바뀌는 전환점

prototype으로 지정된 값들은 여러 개의 Object가 생성이 되었을 때, 같은 값은 공유하는 것일까요? 각각 다른 값을 가지고 있는 것 일까요?

var func = function(name) { this.name = name; }

func.prototype.aa = 10;

func.prototype.check = function () { this.aa.print(this.name); };

 

newFunc1 = new func("o1");

newFunc2 = new func("o2");

func.prototype.aa = 20;

newFunc1.check();

newFunc2.check();

newFunc1.aa = 30;

func.prototype.aa = 40;

newFunc1.check();

newFunc2.check();

> o1: 20
> o2: 20
> o1: 30
> o2: 40

위의 결과 처럼 새로 생성한 Object에서 물려받은 값을 변경하지 않을 때에는 원래의 prototype의 값을 참조하다가 생성한 Object에서 값을 변경하면 이 참조가 깨지게 됩니다. 이는 변수 뿐만 아니라 함수도 적용되는 것이므로 객체의 상속이나 Polymorphism을 구현할 때 알고 있으면 좋습니다.



#4. this를 바꾸는 Call과 Apply

Call()과 Apply()는 파라미터로 Array를 받는지 여부만 차이가 있고 기능은 같은 함수 입니다. 이 함수들은 호출되는 순간의 this를 바꾸는데 사용합니다.

function check(str) { this.aa.print(str); };
var
obj = {

    aa: 10,

    check: check

    };

var func = function() { this.aa = 20; };

func.prototype.check1 = function(str) { obj.check(str); };

func.prototype.check2 = obj.check;

 

var newFunc = new func();

newFunc.check1("indirect");

newFunc.check2("direct");

newFunc.check2.call(obj, "call");

check.apply(newFunc, ["apply"]);

> indirect: 10
> direct: 20
> call: 10
> apply: 20


그럼 즐~Javascript  !!
Posted by U_Seung


이번에는 지난번에 다룬 기본기를 바탕으로 하여서 OOP개념에 기초가 되는 간단한 Class와 Object를 만들어 보았습니다.


#1. 아주 기본적인 Counter

function MakeIncCounter(initialNumber)
{
     return function() { return ++initialNumber; };
}

counter1 = MakeIncCounter(20);
counter2 = MakeIncCounter(10);
counter1();
counter2();
counter1();
counter1().print("Counter1");
counter2().print("Counter2");

> Counter1: 23
> Counter2: 12

1씩 단조 증가하는 Counter를 만들어 보았습니다.
여기에서 재미있게 볼 부분은 initialNumber 부분 입니다. C/C++에서는 함수가 끝나면 함수 내에서 사용하던 변수들이 모두 소멸되지만 Javascript에서는 참조하고 있는 곳이 있다면 함수가 종료 되었다 하더라도 해당 Context의 변수가 소멸되지 않습니다.




#2. 기능이 조금 있는 Counter

앞의 Counter는 단순히 1씩 밖에 더하지 못하는 Counter였습니다. 여기에 기능을 조금 더 추가한다면 다음과 같이 할 수 있겠습니다.

function MakeCounter(initialNumber, steps)
{
     initialNumber = initialNumber || 0;
    
steps = steps || 1;
    
return {
    
     StartingValue: initialNumber,
    
     GetValue: function() { return initialNumber },
    
     Inc: function() { initialNumber += steps; },
    
     Dec: function() { initialNumber -= steps; }
    
};
}

counter = MakeCounter(20, 3);
counter.Inc();
counter.Dec();
counter.Dec();
counter.StartingValue.print("Counter(S)");
counter.GetValue().print("Counter(C)");

> Counter(S): 20
> Counter(C): 17

여기에서는 GetStartingValue와 GetValue의 차이점을 보실 수 있습니다. StaringValue는 MakeCounter()가 return할 때의 initialNumber의 값을 받고, GetValue는 initialValue의 현재 값을 받습니다.



#3. 가독성 높이기

약간의 코드의 가독성을 고려한다면 아래와 같이 작성할 수도 있겠습니다.

function MakeCounter(initialNumber, steps)
{
     initialNumber = initialNumber || 0;
     steps = steps || 1;
     var name;
     var currentCounter = initialNumber;

    
// Methods
     function SetName(value) { name = value; }
     function GetValue() { return currentCounter; }
     function SetValue(value) { currentCounter = value; }
     function GetSteps() { return steps; }
     function Inc() { currentCounter += steps; }
     function Dec() { currentCounter -= steps; }
     function Print() { currentCounter.print(name); }
     return {
          "StartingValue": initialNumber,
          "SetName": SetName,
          "GetValue": GetValue,
          "SetValue": SetValue,
          "GetSteps": GetSteps,
          "Print": Print,
          "Inc": Inc,
          "Dec": Dec
     };
}

counter1 = MakeCounter(100, 20);
counter1.SetName("C1");
counter2 = MakeCounter();
counter2.SetName("C2");
counter2.SetValue(20);

counter1.Dec();
counter1.Dec();
counter2.Inc();
counter2.Inc();

counter1.Print();
counter2.Print();

> C1: 60
> C2: 22


약간 그럴싸한 Class가 만들어 졌습니다. 하지만 Method를 통한 Member variable의 접근은 가능하지만 Property를 통한 접근은 불가능한 상태 입니다. Member variable이 모두 private이라고 생각한다면 문제가 없겠지만 public한 경우도 있을 수 있으니 이를 확장 해보도록 하겠습니다.


#4. This Object 만들기.

 Object의 안과 바깥에서 공통적으로 접근할 수 있도록 하기 위해서 thisObj라는 Object를 생성하고, 여기에 Properties와 Methods를 추가하는 방법을 택하였습니다.

function MakeCounter(initialNumber, steps)

{

     initialNumber = initialNumber || 0;

     steps = steps || 1;

    

     var thisObj = {};

    

     // Properties

     thisObj.StartingValue = initialNumber;

     thisObj.name = "";

     thisObj.currentCounter = initialNumber;

 

     // Methods

     function SetName(value) { thisObj.name = value; }

     function GetValue() { return thisObj.currentCounter; }

     function SetValue(value) { thisObj.currentCounter = value; }

     function Inc() { thisObj.currentCounter += steps; }

     function Dec() { thisObj.currentCounter -= steps; }

     function Print() { thisObj.currentCounter.print(thisObj.name); }

     var methods = {

          "SetName": SetName,

          "GetValue": GetValue,

          "SetValue": SetValue,

          "Print": Print,

          "Inc": Inc,

          "Dec": Dec

     };

    

     for (var name in methods)

         thisObj[name] = methods[name];

     return thisObj;

}

counter = MakeCounter(100, 20);
counter.Inc();
counter.currentCounter -= 15;
counter.currentCounter.print("Counter");

> Counter: 105


 이제 정말 Class다운 모양을 갖춘 것 같습니다. 아쉽게도 Javascript의 언어적인 한계상 C++등의 Compiler단에서 지원하는 private, protected등의 Encapsulation을 깔끔하게 적용하기에는 다소 무리가 있지만 그런대로 OOP 프로그램을 할 수준은 아닌가 생각이 듭니다. 물론 private member와 private static member를 구현한 유명한 Technique[각주:1]들이 있는데 별로 권장할 만한 방법은 아닌 것 같습니다..



#5. Class 기능 확장하기.

OOP에서 가장 기본적인 개념 중 하나가 상속(Inheritance) 입니다. 이 것도 좀 흉내를 내본다면 아래와 같이 할 수 있습니다. 새로 만든 ImprovedCounter는 기존의 Counter가 가진 단순 Inc(), Dec()에 parameter를 지정하여서 특정값을 증가하거나 감소 할 수 있도록 하였습니다.

function MakeImprovedCounter(initialNumber, steps)

{

     var thisObj = MakeCounter(initialNumber, steps);

     thisObj.SetName("Improved");

    

     thisObj._superInc = thisObj.Inc;

     thisObj.Inc = function(steps) {

         if (steps == undefined) thisObj._superInc();

         else {

             thisObj.currentCounter += steps;

         }

     }

    

     thisObj._superDec = thisObj.Dec;

     thisObj.Dec = function(steps) {

         if (steps == undefined) thisObj._superDec();

         else {

             thisObj.currentCounter -= steps;

         }

     }

    

     thisObj.Initialize = function() {

          thisObj.SetValue( thisObj.StartingValue );

     }

    

     return thisObj;

}

var imCounter = MakeImprovedCounter(100, 20);
imCounter.Dec();
imCounter.Initialize();
imCounter.Inc();
imCounter.Inc(-10);
imCounter.Print();

> Improved: 110


이상 여기까지 만들어본 Class들은 Scope에 대한 이해를 돕기 위해서 Javascript에서 지원한 prototype, this, new의 개념을 사용하지 않고 만들었습니다. 하지만 실제 프로그래밍을 한다면 Javascript에서 지원하는 좋은 문법들을 적절히 도입하고, Prototype.js와 같은 라이브러리를 사용하는 것이 바람직 합니다. (참조: Defining classes and inheritance - prototype.js)


Javascript로 OOP를 구현하는 데 참조할 만한 글은....

Object Oriented Programming in Javascript
Classical Inheritance in JavaScript
Classes in Jscript - Part I
Classes in JScript – Part II: Instance Properties / Methods & Class Properties / Methods
Classes in JScript – Part III: Class Hierarchy and Data Encapsulation


Posted by U_Seung



Javascript가 가장 어렵다고 느껴질때가 Scope가 헷갈릴 때가 아닐까 생각이 듭니다.
웬만큼 프로그램좀 짜봤다 싶은 사람도 헷갈리기 십상인게 Javascript Scope 입니다.

시간을 내어서 간단히 정리해보았습니다. 말로 정리하는 것은 정리하는 것도 어렵고, 나중에 보기도 힘들어서 예제를 중심으로 정리 하였습니다. 다른 분들도 도움이 되시기 바랍니다.



#0. 테스트를 위한 함수 추가
Object.prototype.print =
     function (comment) {
          comment = comment || "";
          document.writeln(comment+": "+this);
     };

실전에서 사용하기에는 좋은 방법은 아니지만, 테스트 코드 작성의 편의성을 위해서 아무것이나 print()를 하면 화면에 출력되도록 하는 함수를 만들었습니다. 아래 예제에서 사용되는 함수이니 참조하세요.








#1. var로 선언하지 않은 변수는 이전 Context를 참조한다.

function ScopeTest()

{

    score = 50;

    score.print("2nd");

}

 

var score = 10;

score.print("1st");

ScopeTest();

score.print("3rd");

1st: 10
2nd: 50
3rd: 50



function ScopeTest()

{

    var score = 50;

    score.print("2nd");

}

 

var score = 10;

score.print("1st");

ScopeTest();

score.print("3rd");

1st: 10
2nd: 50
3rd: 10


두 결과가 다르게 나오는 것은 var가 붙고 안 붙고 차이 입니다.

Javascript는 var로 선언한 변수 경우, Execution Context(이하 줄여서 Context)[각주:1]가 생성할 때 해당 Context에 변수를 생성 합니다. 만약 해당 Context에 선언되지 않은 변수명를 사용하면, 이전 Context들을 차례로 뒤지면서 해당 변수명을 탐색 합니다. 최상위 Context에서도 변수를 발견하지 못하면 변수는 undefined 값을 가지고 되고, assignment 구문에서 사용되었다면 해당 변수명을 가진 변수를 새롭게 생성합니다. ( 이때, 변수는 최상위 Context에 생성 됩니다.)

Javascript는 함수 호출이 일어나게 되면 새로운 Context를 하나 생성합니다. 그리고 이 Context는 함수가 선언된 곳 하위에 Chian으로 차례로 연결됩니다. 위의 두 번째 예제에서는 ScopeTest()라는 함수가 호출될 때, 새로운 Context가 생성이 되었고 이는 가장 최상위 Context의 하위에 연결 되었습니다. 이 곳에서 새로운 변수 score를  선언 하였기 때문에 최상위 Context의 score와 다른 변수가 만들어 졌습니다.






#2. 이전 Context에 있는 변수를 사용 하려면 ??

function ScopeTest()

{

    score = 20

    var score = 50;

    score.print("2nd");

}

 

var score = 10;

score.print("1st");

ScopeTest();

score.print("3rd");

1st: 10

2nd: 50

3rd: 10





function ScopeTest(context)

{

    context.score = 20

    var score = 50;

    score.print("2nd");

}

 

var score = 10;

score.print("1st");

ScopeTest(this);

score.print("3rd");

1st: 10
2nd: 50
3rd: 20


Javascript는 해당 Context 내의 위치에서 어디라도 한번이상 변수가 선언(var)되면, Context를 생성할 때 변수도 같이 생성 합니다. (즉, 함수 내부에서 var score를 가장 아래에서 선언을 하나 가장 위에서 선언하나 동일한 효과를 지닙니다.) 따라서, 첫번째 예제는 score 변수가 새롭게 생성 도기 때문에 최상위 Context에 있는 score를 접근할 수 없는 것입니다.

 코드의 가독성을 높이기 위해서는 함수 블럭의 최상위에서 변수 선언을 하는 것이 좋습니다.
 C나 C++처럼 쓰고 싶은 상황이라면, 새 블럭마다 사용하는 변수를 모두 선언 해주는 것이 좋습니다.

이전의 Context에 있는 변수를 접근하려면 어떻게 해야 할까요? 일단 이전의 Context에 접근할 수 있는 직접적인 방법은 없습니다. 위의 두번째 예제에서는 this를 써서 접근하는 것과 같은 흉내를 내보이지만 사실상 this는 (Object를 생성하지 않은 상황이라서) window와 동일한 효과를 지니며, ScopeTest() 함수 내부에서도 역시 context.scorewindow.score, this.score와 모두 동일하다고 볼 수 있습니다. ( 여기에 대한 자세한 사항은 깊게 언급할 수 없어서 넘어 갑니다. )








#3. 함수 파라미터로 선언한 변수들..

function ScopeTest(score)

{

    score = 20;

    score.print("2nd");

}

 

var score = 10;

score.print("1st");

ScopeTest(score);

score.print("3rd");

1st: 10
2nd: 20
3rd: 10


함수의 파라미터로 선언된 변수들은 함수가 호출될 때 생성되는 Context에 새롭게 생성 됩니다. 따라서 위의 예제에서는 ScopeTest() 함수가 호출되는 새로운 Context에 score 변수가 생성되기 때문에 var를 붙이지 않더라도 기존 Context의 변수와 분리됩니다.








#4. Context를 하나 더 만들기...

function ScopeTest1()

{

    var score = 20;   

    ScopeTest2();

    score.print ("2nd");

}

 

function ScopeTest2()

{

    score = 30;

}

 

var score = 10;

score.print("1st");

ScopeTest1();

score.print("3rd");

1st: 10
2nd: 20
3rd: 30

function ScopeTest1()

{

    var score = 20;   
    var
ScopeTest2 = function () { score = 30; };

    ScopeTest2();

    score.print("2nd");

}

 

var score = 10;

score.print("1st");

ScopeTest1();

score.print("3rd");

1st: 10
2nd: 30
3rd: 10


위의 두 예제가 어떻게 다른지 유심히 보시면 아실 것 같습니다. Context가 만들어 질 때, Scope Chain이 어떻게 연결되는지를 판단할 수 있어야 합니다. 중요한 것은 함수가 '어디서 호출 되느냐'가 아니라 함수가 '어디서 선언 되느냐'입니다.

두 번째 부분을 아래와 같은  InnerFunction 형태로도 나타낼 수 있습니다.

function ScopeTest1()
{
    var score = 20;
   
function ScopeTest2()
    {
        score = 30;
    }
    ScopeTest2();
    score.print("2nd");
}


좀 더 직관적으로 볼 수 있도록 도식화 해 보았습니다.
 
사용자 삽입 이미지


 위의 그림과 같이 위의 두 예제에서 ScopeTest2()에서 score 변수는 다른 Context의 것을 참조 합니다. (참조: window는 최상위 Context에 위치한 Global Object를 나타냅니다.  )






더 세부적이고, 기술적인 글은 Microsoft JScript 블로그의 Scope chain of JScript Functions을 참조하시면 도움이 되실 겁니다.


  1. ECMAScript Language Specification a-10 [본문으로]
Posted by U_Seung

얼마전에 C# 3.0으로 만든 QuickSort를 포스팅한 이후로도 C#에 대한 생각을 평소 많이 하고 있다.
다만 C#을 Main 언어로 코딩한다면 좋겠지만 아직은 C++을 많이 쓰는 상황이라서 아쉬움이 많다.

C#의 새로운 Functional Feature들을 보면서 떠오르는 것은 단연 SICP였다.

사용자 삽입 이미지

 이 책은 MIT에서 강의용 교재로 쓰이는 책인데, 얼마전 우리나라에도 번역본이 나와서 인기를 끌고 있으며(컴퓨터 프로그램의 구조와 해석), 최근 있었던 Microsoft DevDays 2007에서도 김명호 박사님의 KeyNote에서 소개 되어서 더욱 유명세를 타기도 한 바로 그 책이다.

 암튼 책에 나오는 몇가지 Sample들을 C#으로 다시 만들어 보았다.
 Scheme은 Type이 없고, 괄호를 중심으로 하는 표현방식이라  조금만 코드가 길어지면 머리가 복잡해지는데 반해, C#은 Type이 분명하고, Syntax도 익숙하여 훨씬 Writablity가 높았다. Type의 축복이랄까;;


 몇가지 예제들을 공유하여 본다. 비교하면서 보는 것도 재미가 있을 것 같다.
 예전에 Javascript로도 만든 것도 참조 하면 괜찮을지 모르겠다.


#1. 간단한 함수 - 홀짝 판단

LISP
(define (odd? n)
 (if (= n 0)
   #f
   (even? (- n 1))))

(define (even? n)
 (if (= n 0)
   #t
   (odd? (- n 1))))


C#
static bool IsEven(this int num)

{

    return (num == 0) ? true : IsOdd(num - 1);

}


static bool IsOdd(this int num)

{

    return (num == 0) ? false : IsEven(num - 1);

}




#2. Prime 판단하기


LISP
(define (smallest-divisor n)
 (find-divisor n 2))
(define (find-divisor n test-divisor)
 (cond ((> (square test-divisor) n) n)
       ((divides? test-divisor n) test-divisor)
       (else (find-divisor n (+ test-divisor 1)))))
(define (divides? a b)
 (= (remainder b a) 0))

(define (prime? n)
 (= n (smallest-divisor n)))


C#
public static bool IsPrime(this int num)

{

    Func<int, int> FindSmallestDivisor = divisor =>

    {

        if (divisor * divisor > num) return num;

        if (num % divisor == 0) return divisor;

        return FindSmallestDivisor(divisor+1);

    };

    return (FindSmallestDivisor(2) == num);

}




#3. 무한대 수열 다루기 - 자연수 만들기

LISP

(define (cons-stream a b) (cons a (delay b)))

(define (stream-car stream) (car stream))

(define (stream-cdr stream) (force (cdr stream)))

(define (integers-starting-from n)
  (cons-stream n (integers-starting-from (+ n 1))))

(define integers (integers-starting-from 1)


C#

static IEnumerable<T> ConsStream<T>(this T t, IEnumerable<T> ts)

{

    yield return t;

    foreach (var nextT in ts)

        yield return nextT;

}


static

IEnumerable<int> IntegerStrartingFrom(int num)

{

    foreach (var item in 
        num.ConsStream(IntegerStrartingFrom(num + 1)))

        yield return item;

}


static IEnumerable<int> Numbers()

{

    return IntegerStrartingFrom(1);

}


static IEnumerable<int> Primes()

{

    return Numbers().Where(n => n.IsPrime());

}



#4. 무한대 수열 다루기(2) - 피보나치 수열 만들기

LISP
(define fibs
  (cons-stream 1
               (cons-stream 1
                            (add-streams (stream-cdr fibs) fibs)))))

(define (add-streams s1 s2)
  (stream-map + s1 s2))

(define (stream-map proc . argstreams)
  (if (null? (car argstreams))
    the-empty-stream
    (cons-stream
      (apply proc (map stream-car argstreams))
      (apply stream-map (cons proc (map stream-cdr argstreams))))))


C#

static IEnumerable<int> Fibs()

{

    foreach (var item in

        ConsStream(1,
        ConsStream(1, Fibs().Plus(Fibs().Tails()))))

        yield return item;

}

 

public static IEnumerable<int> Plus(this IEnumerable<int> ts1, IEnumerable<int> ts2)

{

    return ts1.Map(ts2, (t1, t2) => t1 + t2);

}

 

public static IEnumerable<T> Map<T>(this IEnumerable<T> ts1, IEnumerable<T> ts2, Func<T, T, T> Proc)

{

    foreach (var item in 
        Proc(ts1.Head(), ts2.Head())
        .ConsStream(Map(ts1.Tails(), ts2.Tails(), Proc)))

        yield return item;

}






#5. 공유 변수 - Counter 만들기


LISP

(define counter (make-cnt 0))

(define (make-cnt count)
 (lambda ()
   (set! count (+ count 1))
   count))


C#

public static Func<int> MakeCounter(int initialNumber)
{
    return () => (++initialNumber);
}

static void Main(string[] args)
{
    Func<int> Counter = MakeCounter(10);
    Counter();
    Counter();
    Debug.WriteLine(Counter());

}

위의 결과는 13


Posted by U_Seung

static void Main(string[] args)

{

    var list = new[] { 1, 9, 5, 3, 7, 2 };

    list.WriteLine();

    list.QuickSort().WriteLine();

}


오랜만에 코딩이 들어간 포스팅 이네요
.
잘 모르고 있었는데 C# Functional Feature가 많이 들어 갔더군요
.
그래서 간단히 공부겸 테스트겸 가볍게 코드를 만들어 보고, 이를 공유 합니다
.

만들어본 코드는 Quicksort이며, Main함수의 내용은 위와 같고
,
실행한 결과는 아래와 같습니다.

{ 1, 9, 5, 3, 7, 2 }
{ 1, 2, 3, 5, 7, 9 }


먼저 첫 줄, 별 생각 없이 봐도 integer의 리스트를 만들었다는 것을 알 수 있습니다.

var list = new[] { 1, 9, 5, 3, 7, 2 };

 

다만, 자세히 보면 type이 전혀 선언되지 않았다는 것을 알 수 있습니다.
C# 3.0
에서는 Implicitly typed local variables을 지원해서

javascript
스러운 var a = 3 같은 문법을 지원합니다
.
자세한 것은 문서를 참조 하시고,,,

 

list.WriteLine();

list.QuickSort().WriteLine();

 

두 번째부터 세 번째 줄을 살펴보면 list Object MethodWriteLine() QuickSort()를 호출한 것을 볼 수 있습니다. 여기서 눈 여겨 볼 것은 QuickSort()Writeline()은 원래 있는 Method가 아니라 사용자가 만든 확장 Method라는 겁니다. 여기서 list variable Type Explicit하게 정의 하지 않았지만 C#에서 Type inference를 통해 Type이 결정되는데 여기에서는 int[]로 결정이 되겠지요. , C#에서도 다른 Dynamic Language같이 기존에 존재하는 Type에도 확장 함수(Extension Methods)를 추가할 수 있다는 것 입니다.

 

 

실제 Sort부분의 코드를 보면

 

public static IEnumerable<int> QuickSort(this IEnumerable<int> Ts)

{

    return Ts.Case(

        () => Ts,

        (head, tail) =>

            QuickSort(tail.Where(t => t < head))

            .Concat(new[] { head })

            .Concat(QuickSort(tail.Where(t => t >= head)))

            );

}

 

 

여기서 headtype int, tailtype int[]라는 것을 잘 봐야 합니다. C# 2.0에도 anonymous method가 있었는데 C# 3.0에서는 lambda expressions로 더욱 강화 되었습니다. Type inference feature가 추가 되면서 일일이 Parameters type을 적을 필요가 없게 된 것이 매우 큰 변화라 할 수 있을 것 같습니다. Javascript 등과 비교했을 때도, function(x) { return x; } 라고 지저분하게 적었어야 했는데, 이를 (x) => x 라고 적으면 된다니 코드를 작성하는 입장에서는 매우 반가운 일 입니다.

 

 

아무튼 나머지 코드는.


public static void WriteLine<T>(this IEnumerable<T> Ts)

{

    Debug.Write("{ " + Ts.Head());

    foreach (T t in Ts.Tail())

        Debug.Write(", " + Convert.ToString(t));

    Debug.WriteLine(" }");

}


public static T Head<T>(this IEnumerable<T> Ts)

{

    foreach (T t in Ts)

        return t;

    throw new ArgumentException();

}

 

public static IEnumerable<T> Tail<T>(this IEnumerable<T> Ts)

{

    bool head = true;

    foreach (T t in Ts)

    {

        if (head) head = false;

        else yield return t;

    }

}

 

public static IEnumerable<T> Case<T>(this IEnumerable<T> Ts, Func<IEnumerable<T>> CaseEmpty, Func<T, IEnumerable<T>, IEnumerable<T>> CaseNotEmpty)

{

    if (Ts.Count() > 0)

        return CaseNotEmpty(Ts.Head(), Ts.Tail());

    return CaseEmpty();

}

 

 

참조한 곳은..

C# is a functional programming language

Overview of C# 3.0, C# Version 3.0 Specification

 

------------

 Methods를 좀 뒤져 봤는데.
 Head() 는 First()로
 Tail()은 Skip(1)로 대체할 수 있을 것 같네요.

 

Posted by U_Seung

우리 학교 전산학과 2학년에 Scheme을 배우는 과목이 있다. Scheme은 Functional Programming을 지원하는 언어로 우리가 주로 사용하는 C, JAVA 등의 Procedural programming과 또 다른 세계를 보여준다. 오늘은 비슷한 결과를 낼 수 있는 Javascript로 Scheme 스타일을 좀 따라해봤다.

이 둘 사이에는 여러가지 차이가 있는데 가장 큰 차이는 Functional Language가 Referential transparency를 제공한다는 점에 있다. 자세한 사항을 알아서 찾아보시고;;;

일단 예제 코드를 보자.


var cons = function(a, b) { return function (f) { return f(a, b); } };
var car = function(c) { return c(function(a, b) { return a;}) }
var cdr = function(c) { return c(function(a, b) { return b;})
매우 간단한 세가지 함수를 만들었다.


document.writeln( car(cons("a", "b")) );
document.writeln( car(cdr(cons("a", cons("b", "c")))) );
테스트를 해보면? 결과는 무엇이 나올까?


a
b
대충 짐작 했듯이 cons는 두 값을 pair로 만들어 주고,
car는 첫번째 값을 cdr은 두번째 값을 return하는 매우 심플한 예제다.








var list = function() {
  arg = arguments;
var helper = function(i) {
     if (arg.length == i) return null;
     return cons(arg[i], helper(i+1));
};
return helper(0);
}
var list_ref = function(items, n) {
return (n)? list_ref(cdr(items), n-1) : car(items);
}
var length = function(items) {
return (items)? length(cdr(items))+1 : 0;
}
머리가 아프더라도 마음을 비우고 보면, 가볍게 보인다.


list1 = list(1, 3, list(2, 4, 5), 7, 9);
document.writeln( list_ref(list1, 3) );
document.writeln( length(list_ref(list1, 2)) );
앞에서 만든 cons를 이용해서 list를 만들고, list에 있는 값들을 control하는 함수 들이다.


7
3
결과 위와 같이 7, 3이 출력되게 된다.
왜 그런지는 쉽게 생각할 수 있을 듯 하다.








var $plus = function(a, b) { return a+b; }
var $pair = function(item) {
try { a = car(item); b = cdr(item); } catch(e) { return false; }
return true;
}

var map = function(proc, items) {
return (items)? cons( proc(car(items)), map(proc, cdr(items)) ) : null;
}
var accumulate = function(proc, initial, items) {
return (items)? proc( car(items), accumulate(proc, initial, cdr(items)) ) : initial;
}
var count_leaves = function(t) {
return accumulate($plus, 0,
  map(function(a) { return ($pair(a)) ? count_leaves(a) : 1; }, t)
);
}
마지막은 그래도 재미있는 예제로.. map과 accumulate함수 그리고 이를 이용한 트리의 노드 개수를 세는 함수이다.


document.writeln( count_leaves(list ("+", 5, list("*", list("+", 3, 9) , 4) )) )


7
총 노드가 7개(+, 5, *, +, 3, 9 ,4)  이므로...

더 재미있는 예제들은.. 나중에...
혹은 여기를 참조하세요.
Posted by U_Seung