반응형
블로그 이미지
개발자로서 현장에서 일하면서 새로 접하는 기술들이나 알게된 정보 등을 정리하기 위한 블로그입니다. 운 좋게 미국에서 큰 회사들의 프로젝트에서 컬설턴트로 일하고 있어서 새로운 기술들을 접할 기회가 많이 있습니다. 미국의 IT 프로젝트에서 사용되는 툴들에 대해 많은 분들과 정보를 공유하고 싶습니다.
솔웅

최근에 올라온 글

최근에 달린 댓글

최근에 받은 트랙백

글 보관함

카테고리


반응형
Posted on . Written by

 

Building on What’s There

 

이제 여러분의 함수가 API 에 있게 됐고 동작도 잘 되고 있습니다. 이제 코딩을 하면서 그 함수를 call 할 수 있는데요, API 의 새로운 기능을 알게 되는 건 기쁜 일이죠.

이제 기존에 string 라이브러리 함수들 중에 약간 문제 있는 것을 한번 살펴 볼까요. gsub() 함수는 개발자에게 쌈박한 기능을 제공하죠. documentation 페이지를 한번 보세요. 아마 이 함수는 기본적으로 search and replace 기능을 제공하는 함수라는 것을 아실 수 있으실 겁니다.

 

local str = "Hello banana"
print( string.gsub( str, "banana", "Corona user" ) )
--Outputs: Hello Corona user

 

이 함수의 한계는 key/value 를 테이블에 담아서 사용하게끔 기능을 제공하지는 않습니다. 아래처럼 말이죠.

local searchreplace = {
   Hello = "Success",
   banana = "Corona user"
}

 

이 기능을 가능하도록 하려면 테이블 내의 entry들에 대해 iterate 하기 위해 루프롤 돌려야 합니다. 그리고 각 entry 마다 gsub() 함수를 부르면 되죠.

local str = "Hello banana"
for k,v in pairs( searchreplace ) do
   str = string.gsub( str, k, v )
end

print( str )
--Outputs: Success Corona user

pairs() 함수는 searchreplace table에 있는 각각의 인덱스들을 identify 하고 그 이름과 값을 return 하는 함수입니다. Lua table 을 사전처럼 다룰 때 아주 유용하게 사용할 수 있겠죠. 루프 안의 내용들은 생각하지 마시고 이제 string replacement 를 어느곳에서나 사용할 수 있는 훌륭한 함수를 하나 만드신 것만 생각하세요.


이제 함수 안에 루프를 넣어 볼까요.

local function gsubEx( str, searchreplace )
   for k,v in pairs( searchreplace ) do
      str = string.gsub( str, k, v )
   end
   return str
end

이 함수는 string-replaceing 루프를 어느곳에서나 재사용할 수 있도록 해 줍니다.

gsubEx( str, searchreplace )
--Outputs: Success Corona user

아주 쉽죠?

 

Improving What’s There


이제 이것들을 다 같이 함치고 gsub() 함수에 override 해 보죠. 우선 gsub() 함수를 변수에 넣어야 합니다. 아까 보았듯이 Lua 에서는 아래와 같이 함수를 변수에 넣는 것을 지원해 주죠.

local gsub = string.gsub

이렇게 하면 이제 gsub 변수를 사용해서 gsub() 함수를 call 할 수 있게 된 거죠. 이제 함수를 저장했으니 우리가 만든 함수를 이 변수에 replace 해서 gsub 함수를 overwrite 하기만 하면 됩니다. 이 방법은 이미 다뤘었죠. 실제 gsub() 함수가 사용하는 같은 함수 파라미터들을 사용할 겁니다.

string.gsub = function( s, pattern, repl, n )
end


이 단 두 줄로 우리가 한 것은 gsub() 함수의 copy 를 가지게 된 것이구요 그리고 우리가 만든 함수로 이것을 replace 시킨 겁니다. 이제 여기에 마음대로 로직을 넣을 수 있습니다. 아래 로직으로 패턴 파라미터가 string 일 때와 table 일 때를 체크하게 됩니다.

if ( type(pattern) == "string" ) then
   --do something
else
   --do something else
end

string.gsub() documentation을 잠깐 보시면 이 패턴 파라미터가 항상 string 인 것만 보실 수 있을 겁니다.

패턴 파라미터가 string 이면 original 함수를 call 하면 됩니다.


if ( type( pattern ) == "string" ) then
   return gsub( s, pattern, repl, n )  --call the original function and return whatever it returns
else
   --do something else
end

패턴 파라미터가 string 이 아니면 즉 table 이라서 key/value 의 쌍을 이루는 값들이라면 아래와 같이 간단한 루프를 돌립니다. 이 루프는 else 구문 안에 만들어야 겠죠.

else
   --do something else (loop over the keys and replace them in the input string 's')
   for k,v in pairs( pattern ) do
      s = gsub( s, k, v )
   end
   return s
end

우리의 new parameter 이름과 패턴을 사용하기 위해 루프를 살짝 바꿔줬습니다.

 

 


Putting it Together

이제 이것들을 모두 다 합쳐 보죠. 우리가 override 한것과 new gsub() 함수는 아래와 같습니다.

local gsub = string.gsub
string.gsub = function( s, pattern, repl, n )

   if ( type(pattern) == "string" ) then
      return gsub( s, pattern, repl, n )
   else
      for k,v in pairs( pattern ) do
         s = string.gsub( s, k, v )
      end
      return s
   end
end

이 코드의 결과는 string.gsub() 함수가 우리가 만든 로직으로 replace 되게된 겁니다. 우리가 만든 로직은 파라미터가 일반적인 거면 original 함수를 call 하고 테이블이면 우리가 만든 로직을 사용하는 거죠. 짠! 이제 우리는 코로나에 우리만의 라이브러리를 추가했습니다.

print( string.gsub( "Hello banana", { Hello="Success", banana="Corona user" } )
--Outputs: Success Corona user

이렇게 하면 코로나에서 제공되는 어떤 API 라이브러리에도 여러분이 원하는 기능을 추가하실 수 있습니다.

 

Warning!

아마 이것 저것 하고 싶은 것들이 많이 떠오르시죠? 새로운 기능을 추가하실 때는 아래 내용들을 유념하세요.

 original 함수 변수에 저장하는 것을 잊지 마세요.
 함수 내에서 그 함수를 call 하지 마세요. 그러면 무한 루프에 빠지게 됩니다.
 여러분이 만든 함수 내에서 original 함수를 call 하는 조건을 반드시 넣어 주세요.

이건 약간 advanced topic 입니다. 그러니 여러번 직접 해 보시고 이 사용법이 익숙해 지도록 하세요. 직접 production-ready 코드에 넣어서 사용하는 것은 위험합니다. 충분히 테스트 하고 완전 숙지 한 다음에 사용하세요. 여기에는 많은 smoke 와 mirror들이 있습니다. 잘 못 하면 쉽게 좋은 기능을 잃어버릴 수 있습니다.

 


Examples

여기 여러분들이 흥미로워 하실 만한 유용한 내용들 몇개 소개해 드립니다.

1. “math” library — adding a function to calculate length:

--returns the distance between points a and b
math.lengthOf = function( a, b )
   local width, height = b.x-a.x, b.y-a.y
   return ( width*width + height*height ) ^ 0.5  --math.sqrt( width*width + height*height )
end

 

2. “math” library — adding a function to clamp values:

--returns a value clamped between a range
math.clamp = function( val, low, high )
   if ( val < low ) then return low end
   if ( val > high ) then return high end
   return val
end

 

3. print() function — overriding print() to not print when running on a device:

--override print() function to improve performance when running on device
if ( system.getInfo("environment") == "device" ) then
   print = function() end
end

 

4. “math” library — adding a function to calculate nearest multiples:

--rounds up to the nearest multiple
math.nearest = function( number, multiple )
   return math.round( (number / multiple) ) * multiple
end

 

References

    Variable number of arguments: http://www.lua.org/pil/5.2.html
    Lua closures: http://www.lua.org/pil/6.1.html
    First class values: http://www.lua.org/manual/5.2/manual.html
    Functions: http://www.lua.org/pil/6.html
    String trim() function: http://lua-users.org/wiki/StringTrim
    String recipes: http://lua-users.org/wiki/StringRecipes
    Author’s blog: http://springboardpillow.blogspot.co.uk/2012/04/sample-code.html 

반응형