2016년 12월 22일 목요일
클로저로 단순한 GUI 프로그램을 만들어보았다.
그래서 하나를 짬내서 만들어 보았다. 처음에는 swing을 썼다. 참으로 고통이었다. seesaw라는 라이브러리가 있는 걸 확인하고 seesaw를 살짝 사용했다.
저녁에 짬내서 만들면서 느낀 점은 참으로 힘들었다는 점이다.
나의 능력이 문제인지, 내 뇌가 아직 클로저라는 리스프계열에 적응을 못하고 있는 것인지 잘 모르겠다.
막연히 Swing을 쓰면 되겠지라고 생각했었는데... 정말 자바로 그냥 짰으면 순식간에 만들었을 것이라는 생각이 들었다.
아직 내가 클로저를 짬내서 3개월 정도 해봤는데 어렵다는 것은 내가 아직 클로저가 가지는 개념을 모르고 있다는 생각이 든다.
하지만 확실한 것은 리스프 프로그래밍은 정말 어렵다. 처음으로 하는 GUI를 다루는 것이라 버튼 하나 넣는 것에도 애를 먹었다. 내가 고작 만든 것은 이메일만 필터링해서 나열하는 것이다.
대량메일을 보내기 위해서!
그래서 만약
AAA@AAA
BBB@BBB.com
CCC@CCC.net
뭐 이런게 있으면
BBB@BBB.com, CCC@CCC.net
뭐 이렇게 만들어 주는 것이다. 정말 별거 아닌 것을 만들었다. REPL에서 그냥 치면 바로 나오는 걸 만든 것이다.
그런데 참으로 느낀 점은...
재미있었다.
내 앞에 있는 무언가 보이지 않는 큰 힘이 꿈틀거리는데 잡을 수 없는... 그런 느낌?
잡으려고 마구 휘저었지만
잡지 못했고 별거아닌 노력의 산물만 남았지만
정말 재미있었다.
뭔가 쌓아올리는 느낌?
어쨋든 github로 관리하면서 더 손봐야겠다. (과연 할런지...)
https://github.com/ssisksl77/sman
2016년 12월 21일 수요일
[jvm]04 - Instruction Set Summary
Instruction Set Summary
JVM명령은 1바이트 연산코드(opcode)로 구성되어있다.뒤에 딸려오는 제로(zero) 또는 다른 피연산자(operands)는 연산에 쓰인다.많은 명령어들은 피연산자가 없고 그냥 연산코드만 가지고 있다.예외는 무시하고, JVM인터프리터의 내부 loop는
이렇게 생겼다고 한다. 피연산자의 수와 사이즈는 연산코드에 의해 결정된다. 만약 피연산자의 사이즈가 1바이트보다 크다면?
그것들은 빅엔디안(big-endian order : 데이터의 최상위 비트가 가장 높은 주소에 저장되므로 그냥 보기에는 역으로 보인다.)방식으로 저장된다.
예를들어, 로컬변수 unsigned 16비트 인덱스는 두개의 unsigned 바이트로 저장된다.(byte1 과 byte2).
값은
이다.
Types and the Java Virtual Machine
JVM 명령어 셋의 대부분은 명령의 타입정보를 인코드한다.예를들어 iload명령은 로컬 변수(int값이어야만 한다)의 내용을 피연산자 스택에 로딩한다.
fload는 iload와 똑같이 행동한다.(물론 float값을 사용한다.)
두 명령은 똑같은 구현을 가지고 있지만 서로 다른 연산부호(opcode)다.
- i : int operation
- l for long
- c for char
- f for float
- d for double
- a for reference
[jvm]03 Special Method
1. Special Method
자바로 만들어진 모든 생성자는 instance initialization method로 나타내어진다. (그놈의 이름은
클래스 또는 인터페이스는 최소한 한나의 초기화 메소드가 있다. 그리고 그 메소드를 호출해서 초기화가 진행된다.
그 초기화 메소드는 아규먼트가 없으며 void이다.
2. Exception
JVM에서 예외(exception) 단순히 Throwable, 아님 Throwable의 subclass의 클래스 인스턴스이다. 예외를 던지는 것은 예외가 던져진 곳(point)에서 즉각적 로컬 외(nonlocal) 컨트롤의 이전이 된다. 대부분의 예외는 동기적으로 일어난다. 비동기적인 예외 또한 프로그램 실쟁 중에 일어날 수도 있다.
2016년 12월 15일 목요일
[jvm]02 Run-Time Data Areas
JVM은 다양한 런타임 데이타 지역을 정의한다. 그리고 이것들은 프로그램 실행중이 사용된다. 몇몇 data area는 JVM이 시작할 때 만들어지고 JVM이 꺼질 때 파괴된다. 다른 data area는 thread 별로 있다. thread별 data area는 thread가 시작되고 죽을 때 그 삶을 같이 한다.
1. pc register
JVM은 다수의 스레드의 동시(at once) 실행을 지원한다. JVM의 스레드 각각은 자기 고유의 pc (program counter) register를 가진다. 어떤 시점에서든, 각각의 JVM스레드는 하나의 메소드 코드를 실행하고 있을 것이다. 즉 해당 스레드의 현재 메소드(current method)를 말이다. 만약 method가 native(native의 뜻은 뭐지? -> native method를 말하는 듯)하지 않다면, pc register는 현재 실행되고 있는 JVM명령어의 주소를 포함한다. 만약 메소드가 현재 스레드에 의해 실행되고 있는게 native라면, JVM의 pc register의 값은 undefined다. JVM의 pc register는 returnAddress 또는 native pointer(on specific platform)를 담기에 충분하다.
2. Java Virtual Machine Stacks
JVM Thread는 각각 private JVM stack을 가진다.(thread가 생성될 때 같이 만들어진다.) JVM stack은 frame을 저장한다.(frame은 나중에 나온다.) JVM stack은 C언어에서 만든거랑 유사하다. JVM stack은 push, pop frames를 제외하고 절대로 직접적으로 수정되지 않는다. frames는 힙에 할당되어진다. JVM stack을 위한 memory는 인접해 있을 필요없다.
3. heap
JVM은 JVM Thread가 공유하는 heap이라는 공간을 가지고 있다. heap은 모든 클래스 인스턴스와 배열이 할당되는 런타입 데이터 공간(run-time data area)이다. heap은 JVM이 시작될 때 만들어진다. 오브젝트를 위한 힙 저장소는 storage managerment system(가비지컬렉션)에 의해 되돌려 받는다. heap은 고정된 사이즈 또는 확장되도록 할 수도 있다.
4. Method Area
JVM이 공유하는 method area는 class의 구조 (such as the run-time constant pool, field and method data, and the code for methods and constructors, including the special methods used in class and instance initialization and interface initialization.)를 담는다. method area는 JVM이 시작할 때 만들어진다. 비록 논리적으로 heap의 부분이다.
5. Run-Time Constant Pool
class파일 안에 per-class, per-interface의 런타임 constant_pool테이블을 보여주는 곳. (constant,ranging from numeric literals known at compile-time to method and field references that must be resolved at run-time이 들어있음) The run-time constant pool은 일반적인 프로그래밍 언어에 심볼테이블(symbol table)과 유사한 것이라 생각하면 된다. 물론 이건 그것보다 더 많은 범위를 가지고 있다. 각각 run-time constant pool은 JVM의 method area에 할당되어 있다. class나 interface의 run-time constant는 걔네들이 만들어질때 JVM이 구성해준다.
5. Native Method Stacks
JVM은 일반적인 stack을 이용한다. 구어체로 "C tacks"라 부른다. 왜 stack을 사용하냐면 네이티브 메소드(native methods)를 지원하기 위해서다.(자바가 아닌 언어로 씌여진 메소드) 네이티브 메소드 스택은 JVM 명령어 모음의 인터프리터 구현(C언어로)으로 사용된다.
6. frames
프레임(frame)은 데이터와 부분 결과를 저장하려고 사용한다. 뿐만 아니라 dynamic linking, return values for methods, and dispatch exceptions. 이럴 때 사용된다. 새로운 프레임은 메소드가 호출될 때마다 각각 만들어진다. 그리고 메소드 호출이 완료되면 죽는다.(그것이 성공이건 익셉션이건...) 프레임은 각자 로컬변수 배열이 있고, 피연산자 스택 그리고 현재 메소드의 클래스의 런타임 constant pool의 참조.. 가 있다.
7. Local Variables
frame은 각각 local variables 배열을 가짐. 길이는 컴파일 타임에 결정 single local variable은 boolean, byte, char, short, int, float, reference, or returnAddress. A pair of local variables은 long or double. 타입을 가진다.
8. Operand Stacks
각각 frame은 last-in-first-out(LIFO)stack 을 가진다(known as its operand stack). 한가지 알아야 할 점은 double, long은 스택이 두개가 하나다. 그래서 스택에 쌓일 때 두개씩 쌓인다.
[jvm]01 Type and Values
1. Integral Types and Values The values
The values of the integral types of the Java Virtual Machine are:
- For byte, from -128 to 127 (-27 to 27 - 1), inclusive
- For short, from -32768 to 32767 (-215 to 215 - 1), inclusive
- For int, from -2147483648 to 2147483647 (-231 to 231 - 1), inclusive
- For long, from -9223372036854775808 to 9223372036854775807 (-263 to 263 - 1), inclusive
- For char, from 0 to 65535 inclusive
2. Floating-Point Types, Value Sets, and Values
부동소수점 타입(floating-point types)은 float과 double이 있다. 둘은 각각 개념적으로는 'IEEE 754 표준'에서 규정하는 32-bit single-precision과 64-bit double-precision 포맷이다.
IEEE 754 standard는 +/- 수만 포함하지 않고 +/- 0(제로값), +/-무한값, 그리고 Special Not-a-Number(NaN)값. NaN값은 0/0같은 유효하지 않은 연산의 결과를 표현할 때 쓰인다. 모든 JVM의 구현은 두개의 표준 집합을 지원하는 것이 요구된다. (float value set과 double value set)
returnAddress Type and Values
returnAddress type은 JVM의 jsr, ret 그리고 jsr_w 명령어에서 사용한다. returnAddress의 값은 JVM명령어들의 연산부호의 포인터들이다. 숫자 원시 타입(numeric primitive types)와 달리 returnAddress타입은 어느 자바 프로그래밍 언어 타입과 들어맞지 않고 running program에 수정될 수 없다.
3. boolean Type
비록 JVM이 불린타입을 정의한다 하지만, 아주 제한적인 것만을 지원한다. JVM에는 boolean 값을 위해 만들어진 명령어는 없다. 대신 JVM에 불린값을 int로 바꾸는 어구가 따로 있다. JVM은 boolean arrays는 직접적으로 지원한다. newarray 명령어는 불린타입배열를 만들 수 있다. 불린타입배열은 바이트배열(byte array)의 명령어로 접근되고 수정된다.(baload, bastore) *오라클 JVM의 구현에서는, 불린배열은 JVM 바이트배열로 인코딩 되어있다.(불린요소 하나당 8비트) JVM은 1,0을 인코딩해서 불린배열을 사용한다.(1=true, 0=false) 자바의 불린값이 int타입 값으로 매핑된다.(compiler가 함)
4.Reference Types and Values
reference type에는 3가지가 있다. : class type, array type, interface type 이것들의 값은 각각 동적으로 만들어지는 클래스 인스턴스, 배열 또는 인터페이스를 구현한 클래스 인스틴스 / 배열과 연결된다. 배열타입은 1차원 컴포넌트타입을 구성한다.(???) 배열타입의 컴포넌트 타입은 자기 스스로 배열 타입이다.(???) 만약, 어떤 배열타입에서 시작하면, 하나는 컴포넌트 타입을 고려하고, 그 후에 그 타입의 컴포넌트타입(이것도 배열이라면)을 고려한다. 계...속, 마침에(마지막에) 배열타입이 아닌 컴포넌트타입이 발견된다. 이걸 element type of an array type 이라고 한다. 배열의 요소타입이라고 하자. 이 요소타입은 원시 타입, 클래스 타입, 인터페이스 타입건 상관 없지면 이 이중에 있어야 한다. *여담: 컴포넌트타입은 확장이 가능하니 배열을 확장할 때 component type으로 확장되다가 값이 있는 element type으로 가면 거기서 멈추는 듯. 레퍼런스 값은 null reference를 가질 수도 있다. null reference는 오브젝트가 없음과 연결되어 있다. (null이란 말) null reference는 처음에는 run-time type을 가지지 않는다. 하지만 어떤 타입으로도 캐스팅 될 수 있다. 이 타입의 디폴트 값은 null이다. 이 명세는 구체적인 null 값을 강제하지 않는다.
[jvm]00-Class File Format 클래스파일포맷
jvm 개요
Class File Format
Data Types
Primitive Types and Values
- byte, 8-bit 부호붙은 2의보수 정수(8-bit signed two's-complement integers), 디폴트 0
- short, 16-bit 부호붙은 2의보수 정수, 디폴트 0
- int, 32-bit 부호붙은 2의보수 정수, 디폴트 0
- long, 16-bit 부호없는 정수(16-bit unsigned integers), 유니코드 코드포인트(문자의 바이트코드)를 나타내며, UTF-16으로 인코딩되어있다. 디폴트 null code point('\u0000')
- float, whose values are elements of the float value set or, where supported, the float-extended-exponent value set, and whose default value is positive zero
- double, whose values are elements of the double value set or, where supported, the double-extended-exponent value set, and whose default value is positive zero
- 위 내용을 그냥 적은 이유는 아래에 관련 내용이 제대로 설명될 것 같아. 용어를 남겨 놓는다. (적절한 용어도 뭔지 모르겠음, 그냥 float,double 값들이랑, double-extended-exponent는 지수로 표현되는 값들을 말한다. 디폴트는 양수0으로 같다)
- The First Edition of The Java® Virtual Machine Specification did not consider boolean to be a Java Virtual Machine type. However, boolean values do have limited support in the Java Virtual Machine. The Second Edition of The Java® Virtual Machine Specification clarified the issue by treating boolean as a type.
모든 JVM으로 컴파일된 코드는 class라는 파일포맷으로 변환된다.
JVM은 두 가지의 타입을 운영한다. primitive types와 reference types이다. 그에 부응하여, 이 두 타입은 서로 방식으로 값을 저장하고, 아규먼트로 다뤄지거나, 리턴값으로 사용되고 또 계산된다. JVM은 대부분의 타입체킹type checking을 런타임 이전에 한다. primitive type의 값들은 이름표가 붙여질 필요가 없어서 알기 힘들 수 있지만, run time에는 타입을 볼 수 있고, 레퍼런스타입의 값으로 구별할 수도 있다.
JVM도 이런 식으로 알아낼까??? JVM은 명령어 집합이 operand(피연산자)타입을 구별한다. (특정 타입 값을 실행하기 위해서 명령어를 보고 확인한다) 예를들어, iadd, ladd, fadd, dadd 는 모두 JVM에서 수를 더하고 결과값을 생성하는 명령어다. 하지만 각각 특정한 operand type에서만 작동한다. 위의 명령어들은 순서대로 int, long, float, double이다.
JVM은 오브젝트를 지원한다. (여기서 오브젝트는 동적할당 인스턴스 또는 배열 모두를 말한다.) 오브젝트 레퍼런스(a reference to an object)는 Java Virtual Machine type reference로 간주된다. (그냥 reference type이라 간주된다는 말...?) 레퍼런스타입의 값은 오브젝트의 포인터로 생각해도 좋다. 하나의 오브젝트에 여러개의 오브젝트레퍼런스가 존재할 수 있다. (More than one reference to an object may exist.) 오브젝트는 항상 type reference의 값을 통해서 실행되고, 넘겨지고 테스트된다.(Objects are always operated on, passed, and tested via values of type reference.)
Java Virtual Machine이 지원하는 primitive data type은 숫자 타입(numeric types), 불린 타입(boolean type), 그리고 리턴어드레스 타입이다.(returnAddress type)
숫자타입(numberic types)는 정수타입(integral type)과 부동소수점 타입(floating-point type)으로 나뉜다.
정수타입
부동소수점 타입은 IEEE754의 표준을 따른다.
불린타입(boolean type)은 참 거짓을 true, false로 부호화한다. 디폴트 false.
returnAddress타입은 JVM명령어의 연산부호의 포인터다. primitives type에서 유일하게 Java Programming language type과 직접적으로 연관되어 있지 않다.
윈도우 브라우저 호환성 문제
IE는 우리의 주적이다.
여튼 윈도우즈 브라우저의 호환성 문제를 많이 일으키는 것이 IE인데 그 호환성 문제를 없애려는 미봉책 중 하나를 여기에 적어 놓는다.
http-equiv="X-UA-Compatible" content="IE=edge"매타테그를 하나 만들어서 그 안에 이것을 넣어라. (사실 메타태그를 다 넣어서 보여주고 싶은데 안보여짐... 계속 사라지고... 정말 메타태그로 인식하는 것 같음.방법을 알아봐야겠지만 귀찮아서 이렇게 보여줌)
[javascript patterns][Namespace Pattern] 스터디 19
Namespace Pattern
다른 언어를 공부해 본 적이 있다면 바로 느낌이 왔겠지만 네임스페이스패턴(Namespace Pattern)은 타 언어에서 네임스페이스를 정하는 것과 비슷한 일을 한다. 가장 중요한 차이는 namespace 혹은 package라는 문법적 이름을 사용하지 않고 구현을 해야 한다는 점이다.[javascript patterns][Object Creation Pattern] 스터디 18
The JavaScript language is simple and straightforward and often there’s no special syntax for features you may be used to in other languages, such as namespaces, modules,packages, private properties, and static members.자바스크립트에는 namcespace, module, packages, private properties, static members를 만드는 데 새로운 문법이 있지는 않다. 대신 여러가지 방법으로 구현할 수 있다. (우리는 이 패턴들을 알아야 한다. 다른 언어들이 패키지를 가지고 있듯이 자바스크립트에도 패키지를 가지고 있다. 단지 문법이 제공하는 것이 아닌 우리가 직접 구현해야 한다는 점이 다른 점이다. 그러니 익혀야 한다.)
이번 장에서는 객체화에 관련된 패턴들을 배울 것이다.
- namespacing
- dependency
- declaration
- module pattern
- sandbox patterns
[javascript patterns][함수] 스터디 17
지금까지 배운 것을 요약해보자
코드를 더욱 깨끗하게 하는 것은 개발자의 의무이다. javascript는 참으로 깨끗한 코드를 짜기가 힘들다. 그래서 서로다른 js를 만들어서 html에서 따로 관리하는 것이 대부분인 듯하다.하지만 당신이 API를 만든다면? 이름 하나하나에 많은 공을 들일 것이다. 그리고 이름이 없어지길 원할수도 있다. 그 때 우린 익명함수를 사용한다. 즉시실행함수를 사용하여 전역(ex. window)에 수 많은 라벨들을 없애 깔끔한 코드를 만들도록 한다.
하지만 나는 아직 멀었다. 배우면 배울수록 늘어가는 것이 '내가 모르고 있는 것'들의 양인 것 같다.
[javascript patterns][커리(Curry)] 스터디 16
커링(Curring)
function add(x, y) { var oldx = x, oldy = y; if (typeof oldy === 'undefined') { // 부분적인 적용 return function (newy) { return oldx = newy; } } // 전체 인자를 적용 return x + y; } //테스트 typeof add(5); // "function" add(3)(4); // 7 // 새로운 함수를 만들어 저장 var add200 = add(200); add200(10); // 2010
function add(x, y) { if (typeof y === "undefined") { return function (y) { return x + y; } } // 전체 인자를 적용 return x + y; }여기에는 oldx, oldy가 없는데 원래 x는 암묵적으로 클로저에 저장되어 있고, y는 재사용을 하고 있다.
조금더 범용적인 방식으로 처리할 수 있을까?
어떤 함수라도 부분적인 매개변수를 받는 새로운 함수로 변형할 수 있을까?function flexible_curry(fn) { var slice = Array.prototype.slice, stored_args = slice.call(arguments, 1); return function () { var new_args = slice.call(arguments), args = stored_args.concat(new_args); return fn.apply(null, args); }; }되게 복잡해 보이는 이유는 단시 arguments가 배열이 아니기 때문이다. slice를 쓰고 싶은데 arguments는 배열처럼 보일 뿐 배열이 아니기 때문에 var slice = Array.prototype.slice로 slice안에 메소드를 넣었다. 그리고는 slice.call(arguments, 1); argument를 배열의 메소드로 배열처럼 사용하는 것이다. 이해가 안된다면
[1, 2, 3, 4, 5].slice(1); // [2, 3, 4, 5] Array.prototpye.slice.call([1, 2, 3, 4, 5], 1);이제 테스트를 해보자
function add(x, y) { return x + y; } function flexible_curry(fn) { var slice = Array.prototype.slice, stored_args = slice.call(arguments, 1); return function () { var new_args = slice.call(arguments), args = stored_args.concat(new_args); return fn.apply(null, args); }; } var newadd = flexible_curry(add, 5); newadd(5); // 10add(fn)는 클로저로서 값이 휘발성으로 사라지지 않는다. (객체화도 되지 않았는데 어떻게?) 그것이 클로저다. 나중에 좀 더 공부해보도록 하자. 어쨋든 argument에서 첫번째 것만을 잘라내서(slice) sotred_args에 넣기 위해 Array.prototype.slice.call(arguments, 1)을 사용하였다.
그리고 반쪽자리 add(fn)를 반환한다. 그것을 newadd에 넣었고 newadd(5)를 실행하면 더하기를 수행 할 것이다. 여기서 concat은 배열끼리의 접합을 수행한 후, args에 넣는다. 그 후 fn.apply(null, args); 를 실행한다. (apply는 call과 비슷하게 동작하지만 배열을 받는 다는 것을 기억하라. 이 예제에서 apply는 아주 잘 맞는다.)
function multifly(a, b, c, d, e) { return a * b * c * d * e; } function flexible_curry(fn) { var slice = Array.prototype.slice, stored_args = slice.call(arguments, 1); return function () { var new_args = slice.call(arguments), args = stored_args.concat(new_args); return fn.apply(null, args); }; } flexible_curry(multifly, 1, 2, 3)(5, 5); var a = flexible_curry(multifly, 1.534); a(10, 10, 10, 10); var b = flexible_curry(a, 2, 3); b(6, 7);커링은 엄청난 자유를 준다.
커링을 사용해야 할 경우
어떤 함수를 호출할 때 대부분의 매개변수가 항상 비슷하다면, 커링의 적합한 후보라고 할 수 있다. 매개 변수 일부를 적용하여 새로운 함수를 동적으로 생성하면 이 함수를 반복되는 매개변수를 내부적으로 젖아하여, 매번 인자를 전달하지 않아도 원본 함수가 기대하는 전체 목록을 미리 채워놓을 것이다.[javascript patterns][커리(Curry)] 스터디 15
부분적인 적용
함수의 호출이 실제로는 인자의 묶음을 함수에 적용하는 것임을 알 수 있다. 그러면 인자를 한 번에 다 적용하는 것이 아니라 부분부분 인자를 가져와서 함수에 적용하는 것은 어떨까? 이것은 수학 함수를 직접 계산할 때 흔히 쓰는 방법이라 한다.예를 들어보자
funtion add(x, y) { return x + y; } add(5, 4);여기서 부분적인 적용을 하는 개념은
function (5, y) { return 5 + y; } // 이렇게 x를 먼저 적용한 후에 function (5, 4) { return 5 + 4; } // 이렇게 나머지 인자가 부분적으로 가져와서 적용하는 것이다.그러니까 첫번째 인자를 적용한 상태에서 다른 인자를 넣어서 함수를 수행하는 것이다.
var add = function (x, y) { return x + y; } add.apply(null, [5, 4]); // 9 var add_part = add.partialApply(null, [5]); // 첫번째 인자만 먼저 적용 (부분적용) add_part.apply(null, [4]); // 9이것은 사실 add(5)(4)와 같다. add(5)가 (4)로 호출할 수 있는 함수를 반환하기 때문이다. 아! partialApply는 현재 없는 메서드이다. 하지만 이제 만들어 볼 것이다.
함수의 부분적인 적용을 처리하도록 하는 과정을 커링(Curring)이라고 한다.
[javascript patterns][설정 객체 패턴] 스터디 13
설정 객체 패턴
설정 객체 패턴은 좀 더 깨끗한 API를 제공하는 방법이다. 라이브러리나 다른 프로그램에서 사용할 코드를 만들 때 특히 유용하다. 소프트웨어를 개발하고 유지보수하는 과정에서 요구사항이 변경되는 것은 어쩔수 없는 현실이다. 변화하는 요구사항에 우리는 어떻게 코드로 대처해야 할까. addPerson()이라는 함수를 가정해보자. 처음에는 이름과 성만 만들어 사람을 추가하라고 요구를 했다.function addPerson(first, last) {/*...*/}갑자기 생일, 성별, 주소 도 저장해야 한다고 한다.(게다가 선택적으로) 선택적으로 저장하는 것을 모르겠고 일단 매개변수를 추가해보자.
function addPerson(first, last, dob, gender, address) {/*...*/} //선택적인 매개변수는 일부러 뒤에 적었다.함수가 좀 길어졌다. 그런데 username은 필수로 저장해야 한다고 전화가 왔다. 이제 함수를 호출할 때는 필수로 써야 하는 것, 선택적인 매개변수, 그리고 매개변수의 순서... 점점 많아지고 복잡해진다.
addPerson("Younghwan", "Nam", new Date(), null, null, "myID");많은 수의 매개변수는 참 불편하다. 모든 매개변수를 하나의 객체로 만들어 전달하는 것이 더 낫다. 이 객체를 설정(configuration)을 뜻하는 conf라고 해보자.
var conf = { username: "myID", first: "Bruce", last: "Wayne" }; addPerson(conf);이 패턴은 함수가 DOM 엘리먼트를 생성할 때, 엘리먼트의 CSS스타일을 지정할 때 유용하다. 엘리먼트와 스타일은 많은 수의 어트리뷰트와 프로퍼티를 가지며 대부분은 선택적인 값이기 때문이다.
2016년 12월 11일 일요일
[Mathematics for Computer Science] Recurrences 재귀
Recurrences 재귀
재귀를 넘어갔다. 여러가지 내용을 보았는데, 사실 올리기가 너무 힘들다. (기호 찾기가 너무 힘들어서... 꼭 그것만은 아닌 것 같고...)하지만 이번 내용은 꼭 올려놓아야겠다.
The Tower of Hanoi
숫자는 각각의 탑과 그 탑의 크기라고 보면 된다. 바로 위에 보이는 그림은 탑 전체가 이동하기 위해 움직여 온 발자취 같은 것이다. 총 7번을 움직였다. 잘 기억하자 3개의 탑이 움직이는데 일.곱.번. 움직였다.
Finding Recurrence
자! 하노이의 탑에서 재귀적인 움직인이 있는지 없는지 찾아봐야 한다. 탑이 n층으로 이루어져 있다 가정하고, 탑이 옆으로 이동할 때 몇번 움직일까? 이것을 T(n) 이라 하자. 일단 3개의 탑이 옆으로 이동하는 것으로 시작해보자( T(3) ). 어떤 규칙이 있는가? 일단 3번이 움직이려면 1,2번이 움직여야 한다. 그리고 3번이 움직이고 또 1,2번이 움직여서 3번 위로 올라탄다. 잠깐! 1,2번은 마치 2층으로 이루어진 탑과 같다.( T(2) ) T(3) = 2 * T(2) + 1 이렇게 된다. 그리고 이것은 T(n) = 2 * T(n-1) + 1 이런 공식을 유추 한다. 이게 맞는 걸까? 한번 T(2)을 예로들어보자. T(2) = 2 * T(1) + 1 = 2 * 1 + 1 탑이 달랑 하나 있을 때( T(1) )는 한 번만 움직이면 될 것이다.T(n) = 2T(n-1) + 1
2016년 12월 10일 토요일
[clojure] ref 사용하기
ref
이전 시간에 atom을 공부했다. 그런데 왜 ref라는게 필요할까? 값을 보호하는데 atom 하나만 있으면 되는거 아닌가? 하지만 여러개의 행위를 트랜잭션처럼 묶고싶다면? ref를 쓰는 것이 좋다.user=> (def items #_=> #{"backpack" "phone" "card" "water" "dairy"}) #'user/items user=> (defn item-count [item count] #_=> {:name item :count count}) #'user/item-count user=> (def shelves (ref {:name "YH Market" :items (set (map #(item-count % (rand-int 10)) items))})) #'user/shelves user=> shelves #object[clojure.lang.Ref 0x7b5ff2de {:status :ready, :val {:name "YH Market", :items #{{:name "backpack", :count 0} {:name "card", :count 7} {:name "phone", :count 7} {:name "dairy", :count 8}
{:name "water", :count 6}}}}] user=> (:name shelves) nil user=> (:name @shelves) "YH Market" user=> (:items @shelves) #{{:name "backpack", :count 0} {:name "card", :count 7} {:name "phone", :count 7} {:name "dairy", :count 8} {:name "water", :count 6}} user=> (defn buy [shelves] (dosync #_=> (when-let [goods (some #(if (> (:count %) 0) %) (:items @shelves))] #_=> (let [updated-goods (item-count (:name goods) (dec (:count goods)))] #_=> (alter shelves update-in [:items] disj goods) #_=> (alter shelves update-in [:items] conj updated-goods))))) #'user/buy user=> (buy shelves) {:name "YH Market", :items #{{:name "phone", :count 2} {:name "dairy", :count 8} {:name "card", :count 5} {:name "water", :count 6} {:name "backpack", :count 2}}} user=> (buy shelves) {:name "YH Market", :items #{{:name "phone", :count 1} {:name "dairy", :count 8} {:name "card", :count 5} {:name "water", :count 6} {:name "backpack", :count 2}}}진열대를 만들고 거기에 item를 넣는다. 그리고 그 갯수는 임의로(0부터 10까지) 정했다. (buy shelves)로 하나를 사면? 하나가 사라진다. (문제는 대충 만들어서 물건을 고를 수 없음 ㅎㅎ..)
[clojure] atom
왜 아톰이라는 것이 존재 하는가.
user=> (def a 1) #'user/a user=> (inc a) 2 user=> a 1 user=> (def b (inc a)) #'user/b user=> b 2느껴지는가? a는 한번 정의되면 바뀌지 않는다. 대신 새로 정의해야한다. 옛 철학자들은 수를 신처럼 받아들이기도 했다. 값은 변하지 않는다. 이건 불변의 진리다. int a = 0; a++; 이걸 사용하면서 값이 변한다고 믿어왔는가? 값이 변했다고 생각하지만 값은 변한것이 아니다. 시간이 변했을 뿐. 값은 새로 창조되었다. 뭐 이런 이야기로 해도 실재 세상에서도 한 곳에서 값이 변해야 하는 상황이 있다. 그럴때 atom을 사용하는 것이다.
user=> (def my_atom (atom 0)) #'user/my_atom user=> @my_atom 0 user=> (swap! my_atom inc) 1 user=> @my_atom 1대충 무슨 짓을 했는지 알기 바란다. (atom 0)으로 아톰을 생성했고, @를 붙여서 아톰의 값을 한번 봐보았다. 그리고 swap! 이놈으로 값을 변화시켰다. 그리고 다시 @로 값을 보면??? 값이 변했다!! 이 외에도 다른 방법으로 변화를 시키는 애가 있다.
(reset! my_atom 0) 0 @my_atom 0
[clojure] 검증자 (validator)
validator
검증자(validator)는 어떤 상태를 참조로 써도 좋은지 정한다.만약 10을 넘으면 안되게 하는 validator를 만들어보자.
(defn max-ten-validator [{:keys [number]}] (< number 10)) (def n (atom {:number 0 :name "Younghwan"} :validator max-ten-validator)) user=> (swap! n update-in [:number] + 3) {:number 3, :name "Younghwan"} user=> (swap! n update-in [:number] + 3) {:number 6, :name "Younghwan"} user=> (swap! n update-in [:number] + 3) {:number 9, :name "Younghwan"} user=> (swap! n update-in [:number] + 3) IllegalStateException Invalid reference state clojure.lang.ARef.validate (ARef.java:33)
[clojure] date time 날짜 시간
Clojure Date/Time
본 내용은 tutorialspoint를 보고 거의 똑.같.이. 정리한 것이다. 자바를 아십니까? 이 3가지를 이용하여 시간을 뽑아보자. 1 Java.Util.Date 2 java.text.SimpleDateFormat 3 getTime1. Java.Util.Date
(def date (.toString (java.util.Date.))) (println date) "Fri Dec 09 11:21:14 KST 2016"
2. SimpleDateFormat
(def date2 (.format (java.text.SimpleDateFormat. "MM/dd/yyyy") (new java.util.Date))) date2 "12/09/2016"
3. getTime
(import java.util.Date) (def date (.getTime (java.util.Date.))) date 1481251602775
2016년 12월 8일 목요일
[clojure-docs]update-in
update-in
(update-in m [k & ks] f & args) 도큐먼트내용처럼 내용을 바꾼다.(update) 첫번째 아규먼트는 바꿀 자료구조(m) 그 다음은 자료구조 안에 있는 키값([k & ks]) 그리고 f 는 키 값에 걸리는 놈을 실행할 함수! 그 뒤는 그 함수에서 사용할 아규먼트!(def here {:name "Younghwan" :age 26}) (update-in here [:age] * 10) {:name "Younghwan", :age 260}이해 되었길 빈다.
2016년 12월 7일 수요일
[clojure-docs]add-watch
add-watch
나는 클로저에 대해 많이 관심이 많다. 그런데 이 watch에 대해 제대로 알지는 못한다. 내가 아는 것은 watcher로 해당 원자값의 상태를 지속적으로 확인한다는 것이다. 그 시작은 watcher를 더하는 것으로 시작되는 것이다. 관찰(watch)는 인자 네 개를 받는 함수이다. 1. 키 2. 관찰되는 참조 3. 이전 상태 4. 새로운 상태 이것이다. 링크에 있는 첫번째 문장을 보자. Adds a watch function to an agent/atom/var/ref reference. The watch fn must be a fn of 4 args: a key, the reference, its old-state, its new-state. 일단 보면 모르겠는데 일단 이상한 보면 무슨 느낌인지 알 것이다.(def at (atom 0)) #'fwpd.core/at (add-watch at :watcher (fn [key atom old-state new-state] (println "The valueof the atom has been changed") (println "key is " key) (println "atom is " @atom) (println "old-state is " old-state) (println "new-state is " new-state) (if (> new-state 10) (println "TOO BIG!!!!!!") (println "HAHA")))) #object[clojure.lang.Atom 0x20788a98 {:status :ready, :val 0}]자 아톰을 생성했다. 아톰이 뭔지 모른다고? 그냥 값이라고 생각하자. 그런데 아주 안전한 값이다. 그래서 단순히 값을 변경할 수 없다. 바꾸려면 특정 함수를 써야 하는데 그것은 swap! 이다. 중요하기 때문에 느낌표까지 붙어있다. 기억하자. 일단 실행해보겠다.
(swap! at inc) The valueof the atom has been changed key is :watcher atom is 1 old-state is 0 new-state is 1 HAHA 1 ;;10까지 올려보자 (swap! at inc) The valueof the atom has been changed key is :watcher atom is 11 old-state is 10 new-state is 11 TOO BIG!!!!!! 11무엇이 바뀌었는가??? HAHA 에서 TOO BIG!!!!!! 으로 바뀌었다. 바로 watcher가 이렇게 계속 관찰해서 반응한 것이다.
[clojure-docs]merge-with
merge-with
말처럼 합치는 것이다(merge). 그런데 합치면서 무얼하는 것(with)이다. 링크에 있는 내용을 보면서 위에 한 문장을 곱씹자.(merge-with + {:a 1 :b 1 :c 1} {:a 10 :c 13}) {:a 11, :c 14, :b 1}이렇게 병합하면서(merge) 더한다.(with)
2016년 12월 4일 일요일
NodeSchool 워크숍 참석기
10월 29일 노드스쿨 워크숍
지난 10월 29일 노드스쿨 워크숍에 갔었다. 노드제이에스가 대체 뭐가 그리 중헌디. 이 난리 인가 하여 참석하기로 결심했다.
수 많은 사람들이 워크샵에 참석했는데 그 중에 초등학생 정도의 아이들도 참석을 하고 있어서 당황했다.
'요즘은 이렇게 코딩합니다.' promise도 이제 예전말이지요.'하면서 계속 코딩방식을 바꿔가면서 현란하게 코딩을 한다.
나는 신청하지 않았다.
생활코딩의 이고잉님 만세
2016년 12월 3일 토요일
[Mathematics for Computer Science]그래프이론을 들어가며....
똑같았다.영어로 이해가 되지 않았던 것들은 한국말로도 이해가 되지 않았다. 단지 읽기가 더 편하여서 '내 머리가 이해했을걸? 방금 한국말이 들어왔잖아' 라고 편안하게 다음 문장을 읽고 있었다. 사실은 하나도 이해하지 않았는데...
기억에 남는 내용은 [자연수의 정렬성]에 관련된 내용인데 해당 내용은 아무리 생각해도 완벽하게 이해가 되지는 않았다. 그냥 "아... 이런게 자연수의 정렬성이구나..." 정도의 느낌인데, 이것도 맞다고 할 수는 없겠다. 모르는 것이 나올 때마다 낙서하고 포스트잇으로 붙이기도 하고 때론 노트를 찢어서 풀로 붙여가면서 나름 공부를 열심히 했는데, 진심으로 집중하지 못했던 시간이 부족했던 것 같아 아쉽다.
빨리 빨리 넘어가서 다음 책도 읽을 날이 왔으면 좋겠다.
2016년 12월 2일 금요일
[Java Reflection REVIEW] 자바 리플렉션 사용기- 02
JAVA REFLECTION
What if You need to transfer Object to Map which means You are going to relocate your Object's private members to a Map. ALL. previously, We've seen how to do this. I would assume that you've already had my code in first '[Java Reflection REVIEW]자바리블렉션 사용기 -01'Info info = new Info("name", "password", "alias", "address", "strength", "etc1", "etc2"); ObjectToMap(map, Arrays.asList("name", "password", "alias", "address", "strength", "etc1", "etc2"), info);But I want to code like this! Because always all of members in Info move into a Map.
ObjectToMap(map, info);How is it possible. We'll use Reflection and collect perticular methods (which are getters) in invoke them. Firstly, make a method called "ObjectToMap"
private void ObjectToMap(MapYou can see there is a for loop statement. We will get getMethods in Object via getGetters(); Second, create getGettersmap, Object sms) { List methods = getGetters(getMethods(sms), sms); for ( Method method : methods) { map.put(CamelToSnake(dropGet(method.getName())), invokeMethod(method,sms)); } }
private Listfinally, INVOKE!!!!getGetters(final Method[] methods, Object obj) { int len = methods.length; List res = new ArrayList (); for(int i = 0; i < len ; i++) { if(methods[i] != null && methods[i].getName().startsWith("get") && !methods[i].getName().endsWith("Class") ) { res.add(methods[i]); } } return res; } private Method[] getMethods(Object sms) { return sms.getClass().getMethods(); }
private String invokeMethod( Method method, Object sms) { String res = null; try { res = String.class.cast(method.invoke(sms)); } catch (Exception e) { e.printStackTrace(); } return res; }You can test easily. I hope you enjoy my text. Your comments will help my blog. Thank you.
2016년 11월 24일 목요일
[Java Reflection REVIEW] 자바 리플렉션 사용기- 01
I'm a Java developer. As a Java developer. There are many annoying things in workspace while I'm working. Such as Using MAP!!!! Let's say. You're going to put Info data into Map
Info info = new Info("name", "password", "alias", "address", "strength", "etc1", "etc2"); Map map = new HashMapI've found tons of lines of codes In my workspace. As a junior Programmer, By the way I've worked as a programmer just about 5 months, I asked my comapany "How can we fix it? This is really annoying?" But they said "I don't know Why you are asking. This is What it is. It is not an error." So I've tried using Java Reflection. It is very Simple that you can easily use it. first. Create Getters(); map.put("name",info.getName()); map.put("password",info.getPassword()); map.put("alias",info.getAlias()); map.put("address",info.getAddress()); map.put("strength",info.getStrength()); map.put("etc1",info.getEtc1()); map.put("etc2",info.getEtc2());
private String craeteGetMethod(String arg) { StringBuffer buffer = new StringBuffer(10); buffer.append("get"); buffer.append(arg.substring(0, 1).toUpperCase()); buffer.append(arg.substring(1)); return buffer.toString(); }Simple so When You put "name" in it. You'll get "getName". Second. Create Invoking Method
private String invokeMethod( String methodName, Object obj) { String res = null; Method me; try { me = obj.getClass().getMethod(methodName); res = (String)me.invoke(obj); } catch (Exception e) { e.printStackTrace(); } return res; }Third. Gather Method and Invoke All
private void ObjectToMap(MapI should have finished refactoring. but this will be enough in this page.map, List methods, Object sms) { for ( String key : methods) { String methodName = craeteGetMethod(key); invokeMethod(methodName, sms); map.put(key, invokeMethod(methodName,sms)); } }
Info info = new Info("name", "password", "alias", "address", "strength", "etc1", "etc2"); Map map = new HashMapBut It's really slow If you are going yo use reflection to put a massive datas into Database. This is not for you. thank you for stopping by. I will be happy with your comments.(); ObjectToMap(map, Arrays.asList("name", "password", "alias", "address", "strength", "etc1", "etc2"), info);
2016년 11월 23일 수요일
[clojure-docs]assoc-in
assoc-in
assoc-in은 정말 어려운 함수이다. 정말 처음엔 뭐 이런게 있나 싶다.(assoc-in {} [:cookie :monster :vocals] "Finntroll") {:cookie {:monster {:vocals "Finntroll"}}}이것은 잘 보자. {}안에 :cookie 안에 :monster 안에 :vocals 안에 "Finntroll"을 넣는 것이다. 그렇다면 이걸보자. 내가 임의로 비슷한 함수를 만들었다.
((fn [new-board [p1 p2] neighbor] (assoc-in new-board [p1 :connections p2] neighbor)) {} [1 3] 4 ) {1 {:connections {3 4}}}보면 assoc-in은 new-board안에 순서대로 넣는 것이다. {} 안에 1 그리고 그 1안에 :connections 그리고 그 안에 3 그리고 그 값으로 4를 넣은 것이다. 정리하면 assoc의 매개변수는 순서대로 1. 값을 들어갈 곳 2. 키값의 덩어리들 3. 키값 안에 들어가 숨어있을 값 좀 더 이해를 위해 get-in을 사용해보자.
(get-in {1 {:connections {3 4}}} [1]) {:connections {3 4}} (get-in {1 {:connections {3 4}}} [1 :connections]) {3 4}
[clojure-doce][tip] 함수만들기의 이상한 아규먼트
내가 의아하게 본 소스가 이거다.
(defn a [new-board [a b]] ... )뭐 이런 식으로 만들어지는 함수를 보았는데 참으로 신기했다. 그래서 하나씩 다 넣어봤는데 결과를 얻었다. 잘 보자.
(defn a [new-board [a b]] new-board ) (a [1] [2 3]) [1]나머지 a, b도 무엇인지 보자.
(defn a [new-board [a b]] a ) (a [1] [2 3]) 2 (defn a [new-board [a b]] b ) (a [1] [2 3]) 3
[clojure-docs]memoize
memoize
클로저에서 값은 불면하다. 그렇기에 이런 이상한 일이 가능한 것인데 호출한 함수를 메모리에 올려서 기억시키는 일이다. (memoize: 이전에 계산한 값 자체를 저장하여 프로그램 실행 시간을 빠르게 하는 기술)(defn sleep [x] (Thread/sleep 1000) x) (sleep "Mr.KIM") "Mr.KIM" (sleep "Mr.KIM") "Mr.KIM"당연히 둘다 잘 나올 것이다. 단 3초 뒤에 나오는 것이다. 만약 이 3초가 강제로 잠재운 것이 아니라 오랜 시간 동안 계산해서 얻어내야 하는 값이라면?
(def memo-sleep (memoize sleep)) (memo-sleep "Mr.KIM") "Mr.KIM" (memo-sleep "Mr.KIM") "Mr.KIM"값은 같지만 두번째로 호출 할때 3초를 쉬지않고 바로 일을 한다. 왜냐하면 값을 이미 가지고 있기 때문ㅇ
[clojure-docs]Decision-Making 조건문
조건문
tutorial point에는 다섯개의 조건문을 소개한다. if if/do nested if case condif
user=> (defn one? [x] #_=> (if (= x 1) #_=> (println "EQUAL!!!!") #_=> (println "no..."))) #'user/one? user=> (one? 1) EQUAL!!!! nil
if/do
if문의 큰 단점이 있는데 그것은 하나만 할 수 있다는 것이다. 그래서 do를 붙인다. 일단 보자.user=> (defn two? [x] #_=> (if (= x 2) #_=> (do (println "TWO!!!!") #_=> (one? x)) #_=> (do (println "not Two") #_=> (one? x)))) #'user/two? user=> (two? 1) not Two EQUAL!!!! nil여러 개의 일을 do안에 넣는 것이다.
nested if
(defn Example [] ( if ( and (= 2 2) (= 3 3)) (println "Values are equal") (println "Values are not equal"))) (Example)그냥 and를 사용할 수 있다는 점을 기억하기 바란다.
case
case expression value1 statement #1 value2 statement #2 valueN statement #N statement #Defaultcase문이라고 생각하면 될듯하다.
user=> (defn score [x] #_=> (case x #_=> 10 (println "score 10") #_=> (println "not 10"))) #'user/score user=> (score 10) score 10 nil
cond
cond (expression evaluation1) statement #1 (expression evaluation2) statement #2 (expression evaluationN) statement #N :else statement #Default그냥 condition이라 생각하면 편하다
(defn Example [] (def x 5) (cond (= x 5) (println "x is 5") (= x 10)(println "x is 10") :else (println "x is not defined"))) (Example)
[clojure-docs]loop 루프
loops
클로저에서 반복문 덩어리들을 죄다 살펴보자. tutorial point에는 4가지 반복문이 소개되고 있다. while doseq dotimes loopwhile
(while(expression) (do codeblock))while문의 expression이 true인 동안만 codeblock을 실행시키는 것이다.
(let [x 10 y (atom 0)] (while (< @y x) (do (println @y) (swap! y inc)))) 0 1 2 3 4 5 6 7 8 9 nil
doseq
(doseq (sequence) statement#1)doseq는 'foreach'문과 비슷하다. 일단 보면서 foreach문을 연상하자.
(def seq [1 2 3 4 5 6 7]) (defn go [seq] (doseq [n seq] (println n))) (go seq) 1 2 3 4 5 6 7 nil
dotimes
(dotimes [n 10] (println n)) 1 2 3 4 5 6 7 8 9 nil횟수만큼 index가 돈다고 생각하면 된다.
loop
loop [binding] (condition (statement) (recur (binding)))loop는 for-loop를 말하는 것이 아님. loop은 let처럼 값을 바인딩하고 recursion포인트를 지정한다. (그러니 재귀로 돌아갈 곳을 지정한다)
(loop [x 10] (if (< x 1) x (recur (- x 1)))) 0
2016년 11월 21일 월요일
[clojure-docs] ->
->
-> 이건 사실 없어도 된다. 괄호의 지옥에서 벗어나기 위해 만들어진 창과 같은 것이다. 링크에 있는 예제를 보자.(first (.split (.replace (.toUpperCase "a b c d") "A" "X") " ")) "X" (-> "a b c d" .toUpperCase (.replace "A" "X") (.split " ") first) "X"괄호만 있을 때 보다 화살표로 만들어진 것이 더 읽기 쉽다.
2016년 11월 20일 일요일
[clojure-docs]comp
comp
comp함수는 함수 여러개를 같이 실행시키게 되는 것이다.((comp str +) 8 8 8) ;;=> "24"기억해야 할 것은 오른쪽에서 왼쪽으로 실행한다는 것이다 comp를 써서 문자 속성 값을 얻는 방법
(def character {:name "Smooches McCutes" :attributes {:intelligence 10 :strength 4 :dexterity 5}}) (def c-int (comp :intelligence :attributes)) (def c-str (comp :strength :attributes)) (def c-dex (comp :dexterity :attributes)) (c-int character) 10 (c-str character) 4 (c-dex character) 5이게 뭐야? 왜 저런 값이 나오는 거지? 라고 생각할 수 있다. 자 comp가 어디서부터 실행된다고? 오른쪽에서 왼쪽으로
(#(:intelligence (:attributes %)) character) 10 (#(:strength (:attributes %)) character) 4 (#(:dexterity (:attributes %)) character) 5이렇게 되는 것과 같다. 어떻게 이렇게 되는 건가?
(defn two-comp [f g] (fn [& args] (f (apply g args))))
[clojure-docs]assoc
assoc
(assoc {} :key1 "value" :key2 "another value") {:key2 "another value", :key1 "value"}딱 보면 알듯이 {}안에 다 넣는 것이다.
(assoc nil :key1 4) {:key1 4} (assoc {} nil nil) {nil nil}
[clojure-docs][tip] parse
parse
parse에 사용할 만한 아주 단순한 패턴이다.(defn parse "Convert a CSV into rows of columns" [string] (map #(clojure.string/split % #",") (clojure.string/split string #"\n")))순간 이게 뭔가 싶었다.
(clojure.string/split string #"\n")이것만 일단 실행해보자.
자 이렇게 벡터로 만들어진다. 이거에서 하나하나 쉼표로 나누려면 map으로 하는 것이다. 하나씩 나누는 것이다.
[clojure-docs]spit, slurp
slurp
spit은 파일을 쓰는데, slurp는 파일을 읽는 데 사용할 수 있다.(spit "test.log" "hello mate?\n") nil (slurp "test.log") "hello mate?\n"특이한 것은 읽고 쓰는 함수의 이름이 spit (침을 퉷! 뱉는 것 같이 무언갈 뱉어내는 행위) 이라는 것이 흥미롭다. 물론 slurp도 마찬가지.
2016년 11월 19일 토요일
[clojure-docs] apply
apply
apply는 배열로 만들 수 있는 자료 구조를 부시고 각각의 매개변수를 취하는 함수로 전달할 수 있다.(max [1 2 3 4 5 6 7 8 9]) [1 2 3 4 5 6 7 8 9] (apply max [1 2 3 4 5 6 7 8 9]) 9 (max 1 2 3 4 5 6 7 8 9) 9보이는가 apply를 써서 자료구조를 부시고 개별 인자로 함수에 전달을 하게 된다. 이걸로 into함수를 만들어 보자.
(defn my-into [target additions] (apply conj target additions)) (my-into [1] [2 3 4])
[clojure-docs]conj
conj
conj는 into랑 비슷하다. 비.슷.해.서. 헷갈릴 것이다.(conj [] [1]) [[1]]??? 헷갈릴 수가 없을 것 같다.
(conj [1] [3 4]) [1 [3 4]]벡터 [1] 안에 벡터 [3 4]가 들어간 것이다. 만약 [1 3 4]가 되게 하고 싶다면 어떻게 해야 할까?
(into [1] [3 4]) [1 3 4]전에 있던 into를 사용하면 된다. conj와 into로 정의를 할 수도 있다. (그만큼 비슷하다.)
(defn my-conj [target & additions] (into target additions)) (my-conj [1] [3 5 6]) [1 [3 5 6]]
[clojure-docs] into
into
into는 중요한 집합 함수이다. 배열 함수는 대체로 원래 자료 구조가 아닌 배열을 반환한다. 그런데 원래 배열이나 특정배열로 변환하여 반환하려면 into가 필요하다.(map identity {:a "AAAA!!!"}) ([:a "AAAA!!!"]) (into {} (map identity {:a "AAAA!!!"})) {:a "AAAA!!!"}
(map identity [:I :MY :ME]) (:I :MY :ME) (into [] (map identity [:I :MY :ME])) [:I :MY :ME]
(map identity [:AAA :AAA]) (:AAA :AAA)
[clojure-docs]repeat, repeatedly
repeatedly, repeat
(concat (take 8 (repeat "na")) ["Batman!"]) ("na" "na" "na" "na" "na" "na" "na" "na" "Batman!")repeatly는 와 repeat는 다르다. 아래 내용은 repeat으로 한번 돌려봐라. 생각처럼 안돌아 갈 것이다. repeatedly는 각각 계속 실행시킨다.
(take 3 (repeatedly (fn [] (rand-int 10)))) (3 5 6)
[clojure-docs]sort, sort-by
sort, sort-by
딱 봐도, 정렬인 줄 알겠지?(sort [5 4 3 2 6 1 3 4 2 6]) (1 2 2 3 3 4 4 5 6 6)sort-by는 무언가 (다른 거)에 정렬이 되는 것
(sort-by count ["aaa" "bb" "c"]) ("c" "bb" "aaa")
[clojure-docs]some
some
some은 처음에 나오는 함수가 참인 경우가 하나라고 있는지 물어보는 것이다.(some #(> % 5) [1 2 3 4]) nil (some #(> % 3) [1 2 3 4]) true여기 나오는 값이 실제 값이 아닌 true인 것을 유의하자. 트릭으로 and를 쓰면 된다. (and가 매크로라서(?) 되는듯 나중에 더 공부하고 적어보기로 하겠다.)
2016년 11월 15일 화요일
[clojure-docs]filter
filter를 쓰면 참인 것만 추려내서 반환한다.
(filter #(< % 3) [1 2 3 4 5 6]) (1 2)여기서 take-while과 filter의 차이는 무엇일까. take-while은 값이 정렬되어있으면 불필요한 자료를 검사하지 않고 결과 값을 반환한다. (정렬이 되어 있을 때는 take-while이 더 효율적인 듯 싶다.)
[clojure-docs]take-while
take-while
(def food-journal [{:month 1 :day 1 :human 5.3 :critter 2.3} {:month 1 :day 2 :human 5.1 :critter 2.0} {:month 2 :day 1 :human 4.9 :critter 2.1} {:month 2 :day 2 :human 5.0 :critter 2.5} {:month 3 :day 1 :human 4.2 :critter 3.3} {:month 3 :day 2 :human 4.0 :critter 3.8} {:month 4 :day 1 :human 3.7 :critter 3.9} {:month 4 :day 2 :human 3.7 :critter 3.6}])take-while을 써서 특정 자료만 얻을 수 있다. 1,2월 자료만 가져온다.
(take-while #(< (:month %) 3) food-journal) ({:month 1, :day 1, :human 5.3, :critter 2.3} {:month 1, :day 2, :human 5.1, :critter 2.0} {:month 2, :day 1, :human 4.9, :critter 2.1} {:month 2, :day 2, :human 5.0, :critter 2.5})비슷한 drop-while이라는 것도 기억하자.
(drop-while #(< (:month %) 3) food-journal)이 둘을 함께 써서 2월과 3월의 자료만 얻을 수 있다.
(take-while #(< (:month %) 4) (drop-while #(< (:month %) 2) food-journal))
[clojure-docs]take, drop
take
take는 두개의 인자를 받는다. 일단 한번 보자.(take 2 [ 1 2 3 4 5]) (1 2)drop 또한 두개의 인자를 받는다.
(drop 3 [1 2 3 4 5 6 7]) (5 6 7)둘이 서로 비슷한 함수다.
2016년 11월 14일 월요일
[clojure-docs]reduce
reduce
process each element in a sequence and build a result(배열의 원소를 각각 처리)(reduce + [1 2 3 4]) 10이 말은 이거와 같다.
(+ (+ 1 2) 3) 4)끔찍하지 않은가?? 다음은 초기값을 지정하고 reduce를 실행하는 모습
(reduce + 100 [1 2 3 4])
2016년 11월 13일 일요일
[clojure-docs]loop
loop
이것도 말그대로 loop문을 만드는 것이다. 하지만 우리가 생각하는 index를 가지고 for문을 도는 것이 아니라. 재귀를 한다. 한번 어떻게 되는건지 보자.(loop [x 10] (when (> x 0) (println x) (recur (- x 1)))) 10 9 8 7 6 5 4 3 2 1 nil위의 예제는 x=10이 초기값으로 시작한다. recur는 재귀를 실행한다.
[clojure-docs]zipmap
zipmap
값을 연결해서 맵을 만들어 준다.(zipmap [:a :b :c :d] [1 2 3 4 5]) {:a 1, :b 2, :c 3, :d 4}그럼 map이랑 차이는 뭘까? map은 함수를 계속 실행시킨다. 그 값을 리스트로 반환한다.
덧붙여서 다른 질문들을 둘러보자.
zipmap, map vector의 차이를 찾고 있다.
(zipmap [:k1 :k2 :k3] [10 20 40]) => {:k3 40, :k2 20, :k1 10} (map vector [1 2 3] [4 5 6] [7 8 9]) => ([1 4 7] [2 5 8] [3 6 9])스택오버플로우에서는 그 차이를 이렇게 말한다.
Use (zipmap ...) when you want to directly construct a hashmap from seperate of keys and values. The output is a hashmap.
Use (map vector...) when you are trying to merge multiple sequences, The output is a lazy sequence of vectors.
[clojure-docs]dotimes
dotimes
말그대로 여러번 계속 실행하도록 하는 것.(dotimes [n 5] (println "n is" n)) n is 0 n is 1 n is 2 n is 3 n is 4 nil
[clojureapi]juxt
juxt
juxtaposition : 병렬, 병치, 나란히 놓기 api doc에 잘 설명되어 있다. juxt 안에 있는 함수들을 나란히 놓는 것이다. ((juxt a b c) x) => [(a x) (b x) (c x)]((juxt :a :b) {:a 1 :b 3333 :c 3234 :d 234}) [1 3333]왜 이렇게 나오는 걸까 이유는
[(:a {:a 1 :b 3333 :c 3234 :d 234}) (:b {:a 1 :b 3333 :c 3234 :d 234})]이렇게 각각 함수(:a :b)를 호출하는 것이다.
[clojureapi]doseq
doseq
body의 내용을 각각 실행시킨다. api에 적혀있는 예제를 보면(doseq [x [-1 0 1] y [1 2 3]] (prn (* x y))) -1 -2 -3 0 0 0 1 2 3 nil이렇게 각 벡터를 x,y라 하고 각각 계산한다. 총 값을 9개가 나온다.
[Mathematics for Computer Science][Division Algorithm] 스터디 07
The Division Algorithm
If one number does not evenly divide another, then there is a remainder" left over. More precisely, if you divide n by d, then you get a quotient q and a remainder r.어떤 수가 나누어지지 않을 때 (즉 소수일 때?) 그 수는 나머지를 가진다. 그리고 몫도 알 수 있다. 이 말은 y = ax + b가 된다는 말.
Theorem 25 (Devision Algorithm)
Let n and d be integers such that d 0. Then there exists a unique pair of integers q and r such that n = qd + r and 0 ≤ r d. 위의 y = ax + b와 비슷한 말이다. 여기서 문제는 저 여기서 a와 b의 조합이 단 하나라는 점이다. 어째서 그러한 지 보자. Proof. We must prove that the integers q and r exist and that they are unique.For existence, we use the well-ordering principle. Fisrt, we show that the equation n = qd + r holds for some r ≥ 0. If n is positive, then the equation holds when q = 0 and r = n. If n is not positive, then the equation holds when q = n and r = n(1-d) ≥ 0. Thus, by well-ordering principle, there must exist a smallest r ≥ 0 such that the equation holds. Furthermore, r mush be less than d; otherwise, b = (q +1)d + (r-d) would be another solution with a smaller nonnegative remainder, contradicting the choice of r.Well-ordering Principle 자연수의 정렬성 - 공집합이 아닌 모든 자연수 집합의 부분집합은 하나의 최소 정수를 포함한다는 정리 S is subset of T ∀S such that S ⊆ ℕ and S ∅,∃a ∈ S such that a ≤ b for all b ∈ S
즉, 최소항이 존재한다는 말 자연수 1 2 3 4 ... (공집합이 아닌 부분집합을 생각하라) 예를 들어 1.5보다 큰 자연수의 집합 -> 최소항 '2'가 존재한다.
Now we show uniqueness. Suppose that there exist two diffent pairs of integers q1, r1 and q2, x2 such that:자연수의 정렬성(well-ordering principle)에 의하면 무조건 0보다 크거나 같은 r이 존재한다.(음수건 양수건) 그리고 r은 d보다 작아야 한다. (당연하지 나머지니까) 만약 아니면 값이 엄청 많아진다. 유니크하지 않다는 것! 유니크하다는 것을 말해주기 위해 같은 값에 두개의 조합이 있다고 가정하는 것이다. (바로 위에 만든 것처럼)
n = q1d + r1 (where 0 ≤ r1 d)
n = q2d + r2 (where 0 ≤ r2 d)
Subtracting the second equation from the first gives:
0 = q1 - q2d + (r1 - r2)
The absolute difference between the remainders r1 and r2 must be less than d, since 0 ≤ r1, r2 d. This implies that the absolute value of (q1 - q2)d must also be less than d, which means that q1 - q2 = 0. But then the equation above implies that r1 - r2 = 0 as well. Therefore, the pairs q1, r1 and q2, r2 are actually the same, which is a contradiction. So the quotient and remainder are unique.나머지의 차는 d보다 작아야 한다.(r1,r2의 조건을 보라) 나머지가 d만큼 차이가 나지 않으니까 (q1 - q2)d 또한 0보다 작아야 한다. 이러면 어쩔 수 없이 저 값은 0 이 된다. 그리고 나머지의 차도 0이 된다. 둘은 같은 값인 것이고, 이것은 모순이 된다.(왜냐하면 처음에 두개의 조합을 가정했다.)
[누구나 쉽게 배우는 클로저] [함수(function)][destructuring] study 10
DESTRUCTURING
DESTRUCTURING은 집합에서 이름에 간단히 값을 매기기 위한 아이디어를 바탕으로 한다.
(defn my-first [[first-thing-in-vector]] first-thing-in-vector) (my-first [1 2 3 4 5 6]) 1
my-first 안에 들어있는 인자가 벡터로 되어있는 것을 보자. "이 인자는 벡터!"라는 것을 말해주는 것이다. 이 벡터안에 있는 것들을 마음데로 이름 붙이고 또 나머지 매개변수도 쓸 수 있다.
(defn students [[first second & others]] (print (str "첫 번째 학생: " first)) (print (str ", 두 번째 학생: " second)) (print (str ", 나머지 학생: " (clojure.string/join ", " others)))) (students ["Kim" "Min" "Nam" "Hwang" "Ku"])
이번에는 벡터가 아닌 맵으로 해보자. 맵에 있는 내용과 나의 매게변수와 연결을 할 수 있는데 자세한 것은 밑에서 구경해보자.
(defn coordinate더 간결하게
[{x :x-axis y :y-axis}]
(println (str "x축: " x))
(println (str "y축: " y)))
(coordinate {:x-axis 24 :y-axis 44})
(defn coordinate [:keys {x-axis y-axis}] (println (str "x축: " x-axis)) (println (str "y축: " y-axis))):as를 사용하여 원래의 맵에 접근할 수 있다.
(defn coordinate [:keys [x-axis y-axis] :as args}] (println (str "x축: " x-axis)) (println (str "y축: " y-axis)))
[누구나 쉽게 배우는 클로저] [함수(function)] study 9
클로저 함수 정의하기
조금 더 함수를 정의하는 것에 대해 공부하다. 이번에는 인자의 개수와 무관하게 일을 처리하도록 만들 수 있다. 예를 들자면 전에 보았던 이런 것을 말한다.(+ 1 2 3 4 5 6 6 23 4 2 3 4 2 34 23 4 235 23 42 35 23 42 34 2342 34 23 4 23 42 3) => 3031이렇게 하는 것
(defn check [student] (str student " check!")) (defn attendance-check [& students] (map check students)) (attendance-check "Kim" "Nam" "Park")이러면 여러개의 일을 할 수 있다. 간단하지 않은가?
2016년 11월 10일 목요일
[누구나 쉽게 배우는 클로저] [함수(function)] 스터디 8
함수 호출, 매크로 호출, 특수 형식
앞에서 함수 호출하는 법을 보았다(함수 식을 연산자로 하는 식). 이와 다른 두 가지 형태로는 매크로 호출(macro call)과 특수 형식(special form)이 있다. 일단 다른 이야기보다 함수에 대해 더 다뤄보자.(defn no-params [] "I take no parameters!") (defn one-param [x] (str "I take one paramter: " x)) (defn two-params [x y] (str "Two parameters! That's nothing! Pah! I will smooth them " "together to spite you! " x y))매개변수가 0인 것, 1개인 것, 2개인 것을 나열했다. 클로저의 함수는 개수 덧쓰기(arity overloading)를 지원한다. 즉, 인자 개수에 따라 서로 다른 함수 몸체가 실행되는 함수를 만들 수 있다는 것이다.
(defn multi-arity ;; 3개의 인자와 함수 몸체 ([first-arg second-arg third-arg] (do-things first-arg second-arg third-arg)) ;; 2개 ([first-arg second-arg] (do-things first-arg second-arg)) ;; 1 ([first-arg] (do-things first-arg)))한번 만들어보자.
(defn slap ([name sound] (str name "에게 뺨을 때렸더니? " sound "! 소리가 났다.")) ([name] (str name "에게 뺨을 딱!"))) > (slap "you") "you에게 뺨을 딱!" > (slap "you" "BBAK!") "you에게 뺨을 때렸더니? BBAK!! 소리가 났다."느낌이 오는가?? 그냥 오버로딩 한다 생각하고 괄호로 만들면 된다.
[누구나 쉽게 배우는 클로저] [함수(function)] 스터디 7
((or + -) 1 2 3)(or + -)가 +를 반환하기 때문에 이 식의 답은 6이 된다. 다른 예도 보자.
((and (= 1 1) +) 1 2 3) ((first [+ 0]) 1 2 3)정말 신기 하지 않은가???
(1 2 3 4) ("test" 1 2 3) java.lang.ClassCastException: java.lang.String cannot be cast to clojure.lang.IFn이 에러는 함수가 아닌 것을 함수처럼 쓰려고 할 때 나오는 문장이다. 함수를 인자로 취하거나 함수를 반환하는 함수를 고차원함수(higher-order function)라고 부른다.
고차원 함수 기능이 있는 프로그래밍 언어는 일등급 함수(first-class function)를 지원한다고 하는데, 숫자/벡터 같이 익숙한 자료 유형을 다루는 것처럼 함수를 값으로 취급하기 때문이다.< br />
예를들어보자.
(inc 1.1) (map inc [0 1 2 3])map함수는 집합의 각원소([0 1 2 3])에 주어진 함수(inc)를 적용하여 새로운 목록을 만든다. 클로저가 일등급 함수를 지원하기 때문에 이를 지원하지 않는 다른 언어에 비해 훨씬 강력한 추상화가 가능하다(고 한다...).
2016년 11월 9일 수요일
[누구나 쉽게 배우는 클로저] [집합(set)] 스터디 6
#{:a :b :c}
이것이 집합인데 집합은 좀 특이하다. 집합은 고유한 값의 모임이다. 클로저는 해시 집합과 정렬된 집합을 갖고 있다고 하는데 여기서는 해시집합을 보도록하자.
(hash-set 1 2) => #{1 2}
(conj #{1 2} 2) => #{1 2}
고유한 값이기 때문에 더해도 안 더해진다.
(set [3 3 4 2 3]) => #{2 3 4}
다른 함수들도 알아보자
(contains? #{:a :b} :a) => true
(contains? #{:a :b} 3) => false
(contains? #{nil} nil) => true
(:a #{:a :b}) => :a
(get #{:a :b} :a) => :a
[누구나 쉽게 배우는 클로저] [목록(list)] 스터디 5
'(1 2 3 4) => (1 2 3 4)
리스트는 벡터와 다르게 get함수로 얻어올 수 없다. 대신 nth를 쓴다.
(nth '(:a :b :c) 0) => :a
그리고 요소를 더할 때는?
(conj '(1 2 3) 4) => (4 1 2 3)
여기서 벡터랑 다른 점이 있다. 추가한 4가 맨 처음에 추가 된다는 점이다.
[누구나 쉽게 배우는 클로저] [벡터]스터디 4
[3 2 1]
(get [3 2 1] 0) => 3
이것이 무엇을 뜻하는 것 같은가. 그렇다. get 함수로 0번째 위치를 뱉어낸다. [] 이게 싫다면 이렇게 써도 된다.
(vector 3 2 1) => [3 2 1]
(conj [1 2 3] 4) => [1 2 3 4]
[javascript patterns][설정 객체 패턴] 스터디 13
설정 객체 패턴
설정 객체 패턴은 좀 더 깨끗한 API를 제공하는 방법이다. 라이브러리나 다른 프로그램에서 사용할 코드를 만들 때 특히 유용하다. 소프트웨어를 개발하고 유지보수하는 과정에서 요구사항이 변경되는 것은 어쩔수 없는 현실이다. 변화하는 요구사항에 우리는 어떻게 코드로 대처해야 할까. addPerson()이라는 함수를 가정해보자. 처음에는 이름과 성만 만들어 사람을 추가하라고 요구를 했다.function addPerson(first, last) {/*...*/}갑자기 생일, 성별, 주소 도 저장해야 한다고 한다.(게다가 선택적으로) 선택적으로 저장하는 것을 모르겠고 일단 매개변수를 추가해보자.
function addPerson(first, last, dob, gender, address) {/*...*/} //선택적인 매개변수는 일부러 뒤에 적었다.함수가 좀 길어졌다. 그런데 username은 필수로 저장해야 한다고 전화가 왔다. 이제 함수를 호출할 때는 필수로 써야 하는 것, 선택적인 매개변수, 그리고 매개변수의 순서... 점점 많아지고 복잡해진다.
addPerson("Younghwan", "Nam", new Date(), null, null, "myID");많은 수의 매개변수는 참 불편하다. 모든 매개변수를 하나의 객체로 만들어 전달하는 것이 더 낫다. 이 객체를 설정(configuration)을 뜻하는 conf라고 해보자.
var conf = { username: "myID", first: "Bruce", last: "Wayne" }; addPerson(conf);이 패턴은 함수가 DOM 엘리먼트를 생성할 때, 엘리먼트의 CSS스타일을 지정할 때 유용하다. 엘리먼트와 스타일은 많은 수의 어트리뷰트와 프로퍼티를 가지며 대부분은 선택적인 값이기 때문이다.
2016년 11월 5일 토요일
[Mathematics for Computer Science] 스터디 06
Logical Deductions
Logical Deductions는 논리적 추론이라고 할 수 있는데 여기 중요한 기호들이 나온다.One fundamental inference rule is modus ponens. This rule says that if P is true and P ⇒ Q is true, then Q is also true. Inference rules are sometimes written in a funny notation.
Modus ponens is closely related to the proposition (P ∧(P ⇒ Q)) ⇒ Q. Both in some sense say, "if P and P ⇒ Q are true, then Q is true". This proposition is an example of tautology, because it is true for eery setting of P and Q. The difference is that this tautology is a single proposition, whereas modus ponens is an inference rule that allows us to deduce new propositions from old ones. However, if we accept modus ponens then a eneral theorem of logic says that for each tautological implication there is an associated inference rule.예를들어 ((P ⇒ Q) ∧ (Q ⇒ R)) ⇒ (P ⇒ R) and ((P ⇒ Q) ∧ ¬Q) ⇒ ¬P 둘다 tautologies(동어반복, 항진식)이다. 말로 풀어쓰면 P가 Q이고, Q가 R이라면, P는 R이다. 은 P가 Q이고 Q가 R이 아니라면(false라면) P는 R이 아니다(false다)라는 말과 동일하다.
여기서 ¬이것은 not을 나타낸다.
2016년 11월 4일 금요일
중간리뷰
[여인의 초상]을 읽고
이사벨은 그 시대의 미국인 여인의 초상인 듯 하다. 자유분방함을 원하고 결혼을 구속이라 원하며 결혼으로 인한 신분상승만이 여인의 답이라 생각하지 않으며 항상 생기넘치는 기운을 풍기는 사람. 하지만 이사벨은 결국 결혼을 한다. 정략결혼? 아니다. 아이러니한 것이 자신이 간절히 원하여 결혼을 하게 된다. 그녀를 정말 사랑했던 사람들, 자신들이 사회에 주어진 일들을 충실히 하는 사람들을 거절하고 그녀가 상속받은 돈을 사랑한 사람과 결혼한다. 아주 행복하게...
그녀는 후회하고 후회한다. 그녀는 이미 족쇄를 찼고, 신사들은 그녀를 보며 더이상 행복한 마음을 가지지 않는다. 왜냐하면 그녀의 생기는 갇혀서 퍼지지 못하고 그녀 자신도 더 이상 행복하지 않기 때문이다. 자유? 그런 것들을 생각할 겨를도 없는 듯 하다.
그녀를 끝까지 도와준 렐프. 아 정말 가여운 렐프. 이사벨에게 큰 돈을 안겨주고 떠나간 렐프. 그는 어쩌면 자신이 안겨준 큰 돈 때문에 이사벨에게 이런 비극이 왔다고 생각하기까지 한다. 그리고 몇 년지 죽는 렐프를 보며 이사벨은 렐프를 보고, 다시 살기위해 이혼을 한다.
.
.
.
이 글을 한 장, 한 줄거리로 다 표현할 수는 없다. 이 책은 줄거리보다도 그들의 독백, 가끔씩 하는 말 한마디가 어쩌면 책을 전부 표현하기도 한다. 하지만 우리가 알아야 하는 점은 자신을 너무 믿지 말라는 점. 그러기 위해서는 더욱 배워야 한다는 점. 이사벨은 참으로 당돌하고 매력이 넘쳤지만 많이 부족했다. 자신의 논리를 더욱 믿지 말았어야 했지만... 그녀는 자신을 너무 믿었고, 자신을 사랑하는 사람들을 너무 믿지 않았다. 자신은 당연히 사랑받아 마땅했다고 생각했었을까? 그 사랑이 다 없어져서야 슬퍼하는 삶이란...
[javascript patterns][메모이제이션(Memoization) 패턴] 스터디 12
함수프로퍼티 - 메모이제이션(Memoization)패턴
함수는 객체이기 때문에 프로퍼티를 가질 수 있다. 사실 함수는 생성될 때부터 프로퍼티와 메서드를 가지고 있다. 그 예가 length다. length는 함수가 받는 인자의 개수를 값으로 가진다.function func(a, b, c) {} console.log(func.length); // 3언제들지 함수에 사용자 정의 프로퍼티를 추가할 수 있다. 함수에 프로퍼티를 추가하여 결과(반환 값)을 캐시하면 다음 호출 시섲ㅁ에 복잡한 연산을 반복하지 않을 수 있다. 이런 방법을 메모이제이션 패턴이라고 한다.
var myFunc = function(param) { if (!myFunc.cache[param]) { var result = {}; //... 비용이 많이 드는 수행 ... myFunc.cache[param] = result; } return myFunc.cache[param]; }; //캐시 저장공간cache 프로퍼티는 함수로 전달된 param 매개변수를 키로 사용하고 계산의 결과를 값으로 가지는 객체(해시)다. 위 코드의 문제는 myFunc함수가 단 하나의 매개변수를 받는다고 가정하고 있다. 게다가 이 매개변수는 문자열과 같은 원시 데이터 타입이라고 가정한다.
더 많은 매개변수와 복잡한 타입을 갖는다면 어떻게 해야 할까? (직렬화하여 해결한다.)
var myFunc = function() { var cachekey = JSON.stringify( Array.prototype.slice.call(arguments)), result; if (!myunc.cache[cachekey]) { result = {}; //비용이 많이 드는 수행... myFunc.cache[cachekey] = result; } }; // 캐시 저장공간 myFunc.cache = {};직렬화하면 객체를 식별할 수 없게 되는 것을 주의하라. 만약 같은 프로퍼티를 가지는 두 개의 다른 객체를 직렬화하면, 이 두 객체는 같은 캐시 항목을 공유하게 될 것이다.
이 함수를 작성하는 다른 방법으로는 하드코딩 대신 arguments.callee를 사용해 함수를 참조할 수 있다. (하지만 곧 사라질 운명이다. 쓰지 않는 것이 낫다. 명심)
var myFunc = function (param) { var f = arguments.callee, result; if (!f.cache[param]) { result = {}; //...비용이 많이 드는 수행... f.cache[param] = result; } return f.cache[param]; }; // 캐시 저장공간 myFunc.cache = {}; };
[javascript patterns] 스터디 11
즉시 객체 초기화
코드부터 보자.({ //여기에 설정 값들을 정의한다. maxwidth: 600, maxheight: 400, //메서드도 정의할 수 있다. gimmeMax: function() { return this.maxwidth + 'x' + this.maxheight; }, // 초기화 init: function() { console.log(this.gimmeMax()); //초기화작업실시... } }).init();
초기화 시점의 분기
초기화 시점 분기(로드타임 분기)는 최적화 패턴이다. 브라우저 탐지(또는 기능 탐지)가 전형적인 예다.//변경 이전 var utils = { addListener: function(el, type, fn) { if (typeof window.addEventListener === 'function') { el.addEventListener(type, fn, false); } else if (typeof document.attachEvent === 'function') { el.attachEvent('on' + type, fn); } else { // 구형의 브라우저 el['on' + type] = fn; } }, removeListener: function(el, type, fn) { // 거의 동일한 코드 } };이 코드는 약간 비효율적이다. utils.addListener()나 utils.removeListener()를 호출할 때마다 똑같은 확인 작업을 반복해서 실행한다.
// 변경 이후 // 인터페이스 var utils = { addListener: null, removeListener: null}; // 구현 if (typeof window.addEventListener === 'function') { utils.addListener = function(el, type, fn) { el.addEventListener(type, fn, false); }; utils.removeListener = function(el, type, fn) { el.removeEventListener(type, fn, false); }; else if (typeof document.attachEvent === 'function') { // IE utils.removeListener = function (el, type, fn) { el.detachEvent('on'+type, fn); }; } else { // 구형브라우저 utils.addListener = function (el, type, fn) { el['on' + type] = fn; }; utils.removeListener = function (el, type, fn) { el['on' + type] = null; }; }
[javascript patterns] 스터디 10
즉시 실행 함수의 반환 값
var result = (function () { return '리턴'; }());이렇게 사용하면 된다. 그럼 즉시실행하지 않는 함수와는 무슨 차이일까?
var result = function () { return '리턴'; };이 문법이 더 간단하다. 차이점이라면 result(); 로 해야 값이 나온다는 점. 하지만 혹여 함수가 아니라 '리턴'이라는 값이 나올 것이라는 오해를 할 수도 있다. 그래서 '()'이걸 뒤에 붙이지 않는다면?? 값은 나오지 않는다.
var getResult = { message : (function () { var who = 'me', what = 'call'; return what + ' ' + who; }()), getMsg : function () { return this.message; } }; o.getMsg(); o.message;이 둘을 보면서 message와 getMsg의 사용법의 차이를 생각해보자.
즉시 실행 함수 패턴은 폭넓게 사용된다. 전역 변수를 남기지 않고 많은 작업을 할 수 있게 해줄 것이다. 서적에 의하면 이 패턴은 북마클릿(bookmarklet)에서 많이 쓰인단다. 어떤 페이지에서도 실행되고 전역 네임스페이스를 깨끗하게 유지하는 것이 중요하기 때문이란다.
개별 기능을 독자적인 모듈로 감쌀 때도 유용하다.
개별 기능을 독자적인 모듈로 감쌀 수도 있따. 이걸로 기능을 단위별로 정의하는 거다. 이것을 module1이라고 하자
// module1.js에서 정의한 module1 (function () { // 모든 module1 코드 .... }());이 템플릿을 따라 또 다른 모듈로 코딩할 수 있다. 긜고 실제 사이트에 코드를 올릴 때, 어떤 기능이 사용도리 준비가 되었는지 결정하고 빌드 스크립트를 사용해 해당하는 파일들을 병합하면 된다.
2016년 10월 30일 일요일
[javascript patterns] 스터디 09
즉시 실행 함수
즉시 실행 함수 패턴: 함수 선언 직후 바로 실행되는 문법.(function() { alert('바로 실행'); })();이 패턴은 함수 표현식을 생성한 직후 실행시킨다. 일반 함수에 두개의 괄호가 붙었다. 붙은 위치를 보고 '아! 저렇게 하면 즉시 실행되는 구나!' 하고 느끼기 바란다. 사실 비슷하지만 다르게도 즉시실행을 할 수 있다.
(function() { alert('바로 실행'); }());별 차이는 없다. 하지만 JSLint는 이놈을 더 좋아한다고 한다. 이것을 언제 쓸까? 페이지 로드가 완료 되었을 때, 기본적으로 이벤트 핸들러를 연결하는 등의 초기 설정 작업을 한다. 이런 것들을 사용할 때 이름을 하나하나 전역에 붙였다가는 더러워질 수 있을 것이다.(재사용할 것이 아니기 때문)
(function () { var days = ['일요일', '월요일', '화요일', '수요일', '목요일', '불금세일', '토요일'], today = new Date(), msg = 'Today is ' + days[today.getDay()] + [, ] + today.getDate(); alert(msg); }());
즉시 실행 함수의 매개변수
(function (who, when) { alert(when + ' ' + who + '와 약속이 있습니다.'); }('아무개씨', new Date()));일반적으로 전역 객체가 즉시 실행 함수의 인자로 전달된다. 즉 window를 쓰지 않고 전역 객체에 접근할 수 있다. 이런 방법으로 브라우저 외에서도 코드를 사용할 수 있다. 잘보라
(function (global) { // 전역 객체르 'global'로 참조 하는데 브라우저 상에서는 window였지만 다른 곳에서는 아닐 수도 있는 법 console.log(global); // Window 객체가 뜰 것이다. }(this));다음 글에 이어서 하도록 하자.
2016년 10월 29일 토요일
[Mathematics for Computer Science] 스터디 05
Axioms
Axiom은 공리, 자명한 이치이다.An axiom is a proposition that is assumed to be true, because you believe it is somehow reasonable.Axiom 1. If a = b and b = c, then a = c. 우리가 아주 잘 아는 Axiom이다. A가 B이고 B가 C라면, A = C 이다.
2016년 10월 22일 토요일
[Mathematics for Computer Science] 스터디 04
다른 함의(implication)의 예시를 보자.
만약 개가 날 수 있다면, 당신은 토익을 만점 받을 거야.장난스럽지만 이건 참이다. 설사 지금 당신이 토익이 뭔지 모른다 하여도! 왜냐하면? 만약 개가 날 수 있다면 이 거짓이기 때문에!! 자 truth table을 한 번 보라. P⇒Q 일때 P가 FALSE 이면 Q는 항상 TRUE 라는 것을!(P⇒Q is always true when P is false.)
이 truth table이 마음에 들지 않을 수도 있겠다. 우리가 실제로 생각하는 if-then statements는 다른 것일 텐데 말이다. 그런데 실제 수학에서는 많이 쓰이는 듯 하다.
실망하지 말자 우리가 생각하는 가정문이 곧 나온다.
Proposition 8. ∀n ∈ ℤ (n≥2) ⇔ (n2≥4)
A proposition of the form P ⇔ Q is read "P if and only if Q". (Sometimes "if and only if" is abbreviated "iff".) This proposition is true provided P ⇒ Q and Q ⇒ P are both true. Here is a truth table that compares all these kinds of implication:"iff"라고 불리는 이것이 당신들이 찾던 그것이다. 이것은 P ⇒ Q, Q ⇒ P 둘 다 true일 때 true이다.
2016년 10월 19일 수요일
[javascript patterns] 스터디 08
자기 자신을 정의하는 함수
함수는 동적으로 정의할 수 있고 변수에 할당할 수 있다. 새로운 함수를 만들어 이미 다른 함수를 가지고 있는 변수에 할당한다면, 새로운 함수가 이전 함수를 덮어쓰게 된다. (그러니까 이미 존재하는 함수가 있어도 덮어쓴다는 말)어떻게 보면 함수 포인터를 재사용하는 것이다. 자기 자신을 정의하는 함수은 자기 자신을 덮어써서 재정의(재사용)하는 것이다.
var run = function() { alert("run!"); run = function() { alert("run faster! "); } } // 자기 자신을 정의하는 함수를 사용 run(); // run! run(); // run faster!이 패턴은 함수가 어떤 초기화 준비 작업을 단 한 번만 수행할 경우에 유용하다(한 번쓰고 바로 덮어씌어지기 때문에). 단점은 자기 자신을 재정의한 후, 원본 함수에 추가했던 프로퍼티들을 모두 찾을 수 없다. 또한 객체의 매서드로 사용되면 처음 실행할 때 처음 것이 실행될 수도 있다.(다른 변수에 할당 되었을 시에도 동일)
이 함수를 일급 객체로 사용하는 예를 살펴보자.
// 새로운 프로퍼티 추가 var run = function() { alert("run!"); run = function() { alert("run faster! "); } } run.property = '일급객체로 사용한다.'; // 1. 다른 이름으로 할당한다. var go1 = run; // 2. 메서드로 사용한다. var go2 = { a : run }; // 새로운 이름으로 호출 go1(); //"run" go1(); //"run" console.log(go1.property); // '일급객체로 사용한다.' // 메서드로 호출 go2.a(); // "run" go2.a(); // "run" console.log(go2.a.property); // '일급객체로 사용한다.' // 자기 자신을 재정의한 함수 run(); run(); console.log(run.property); // undefined함수가 새로운 변수에 할당되면 예상과 달리 자기 자신을 정의하지 않는다. 이 호출들은 계속해서 전역 run() 포인터를 덮어 쓴다. 따라서 마지막에 전역 run()가 호출되었을 때 비로소, 변화된 것이 갱신된다.
[javascript patterns][타임아웃] 스터디 07
예시
var delay = function() { console.log('500ms later...'); }; setTimeout(delay, 500);
delay가 괄호없이 변수로 전달된 점에 주의하자. 곧바로 실행하지 않고 포인터만을 전달하는 것이다. 함수포인터 대신 문자열 "delay()"를 전달하는 것은 eval()과 비슷한 안티패턴이다.
2016년 10월 18일 화요일
[javascript patterns][비동기 이벤트 리스터]스터디 06
예시
document.addEventListener("click", console.log, false);
대부분의 클라이언트는 event-driven 방식이다. 페이지 로딩이 끝나면 load 이벤트를 발생시킨다. 사용자는 click, keypress와 같은 이벤트들을 발생시킨다.
자바스크립트가 이런 프로그래밍에 잘 맞는 것은 콜백패턴 때문이다.(비동기적으로 그때그때 동작하는 것이 달라질 것)
2016년 10월 16일 일요일
[javascript patterns][콜백과 유효범위]스터디 05
- 첫번째 리스트
- 두번째 리스트
- 세번째 리스트
- 네번째 리스트
여담인데 혹여 절대로 이 코딩을 따라하지는 않길...
2016년 10월 12일 수요일
[javascript patterns][콜백과 유효범위]스터디 04
var a = function() { callback(parameters); }이 코드는 간단하고 대부분의 경우 훌륭하게 작동한다. 그러나 콜백이 일회성의 익명 함수나 전역 함수가 아니고 객체의 메서드인 경우도 많다.
var a = function() { var myapp = {}; myapp.color = 'green'; myapp.paint = function (node) { node.style.color = this.color; }; var findNodes = function(callback) { //... if (typeof callback === 'function') { callback(found); } //... };
findNodes(myapp.paint);
이러면 작동하지 않는다. findNodes(myapp.paint)를 호출하면 this.color가 정의되지 않아 예상대로 동작하지 않는다.
findNodes()가 전역 함수이기 때문에 객체 this는 전역 객체를 참조한다. findNodes()가 (dom.findNodes()처럼) dom이라는 객체의 메서드라면, 콜백 내부의 this는 예상과는 달리 myapp이 아닌 dom을 참조하게 된다.(this에 대해 자세히 공부를 해야함)
이 문제를 해결하려면, 콜백 함수와 콜백이 속해 있는 객체를 함.께. 전달하면 된다.
findNodes(myapp.paint, maapp);
var findNodes = function (callback, callback_obj) { //... if (typeof callback === 'function') { callback.call(callback_obj, found); } //... };
이것을 바인딩(binding)이라고 하는데 저 위에 사용해본 call(), apply() 가 바인딩을 위해 사용되는 것인데, 나중에 더 공부해보도록 하자.
콜백으로 사용될 메서드와 바인딩할 객체를 전달할 때, 메소드를 문자열로 전달 할 수도 있다. (이렇게 하면 객체를 두 번 반복하지 않아도 된다. 무슨말이냐면!
findNodes(myapp.paint, myapp);여기서
findNodes("paint", myapp);이렇게 바꿀 수 있는데, 이 두 방법에 모두 대응하는 findNodes()를 만드려면?!
var findNodes = function (callback, callback_obj) { if (typeof callback === 'string') { callback = callback_obj[callback]; } //... if (typeof callback === 'function') { callback.call(callback_ovj, found); }