솔리디티에서는 가스비를 최소한으로 아껴야 컨트랙트를 잘 짠다고 할 수 있겠다.
가스비는 이더리움. 돈과 연결되는 부분이므로 줄일 수 있을 만큼 최대한 줄여야 좋은 컨트랙트이다.
먼저 가스비 비교를 위해 아래와 같은 코드를 작성한다.
contract POPvsDELETE {
uint[] public a;
function pushA(uint _n) public {
a.push(_n);
}
function returnA() public view returns(uint[] memory) {
return a;
}
function popA() public {
a.pop();
}
function deleteA() public {
delete a; // 81202, 37288, 25546
}
function renewA() public {
a = new uint[](0);
}
}
pop vs delete
array를 초기화 시키기 위해서 delete는 한번만 수행, pop은 여러번 수행해야하는 차이가 있음.
결과.
pop : 5번 실행 시 213,530 gas 사용
delete : 5번 실행 시 201,268 gas 사용
(string 배열을 이용해서 실험해도 거의 비슷한 결과)
결론.
pop 은 처음 실행부터 마지막 전까지 일정한 cost 유지, 마지막 실행은 많이 듦.
delete 는 처음 실행 시 가스비가 많이 듦. 이후엔 적은 cost 값 유지.
5번 이상 실행 시엔 delete 가 효율적, 4번까지 실행시엔 pop 이 더 효율적.
delete vs renew
요약.
delete와 renew 모두 내부 요소의 개수가 커질수록 cost도 늘어남.
4개의 경우 그리고 10개의 경우 모두 delete가 gas, transaction, execution cost 모두 낮음.
uint[]의 경우에는 delete가 renew 보다 (위 코드에서는) 효율적으로 보임.
1) 숫자를 넣었을 때 그 숫자의 자릿수와 각 자릿수의 숫자를 나열한 결과를 반환하세요. 예시) 3902 > 4 / 3,9,0,2
아래와 같이 함수를 만들었다.
function test1(uint _n) public pure returns(uint, uint[] memory) {
uint num = _n;
uint num2 = _n;
uint a;
for( ; num > 0 ; ) {
a++;
num /= 10;
}
uint[] memory number = new uint[](a);
for(; a > 0 ;) {
number[a-1] = num2 % 10;
a--;
num2 /= 10;
}
return (number.length,number);
}
문제를 보자마자, 자릿수를 하나씩 넣어주려면 uint 형식의 배열. 그리고 배열의 길이를 가져오면 쉽게 풀 수 있을거라 생각했다.
먼저, 받아온 값을 uint 형 지역변수 num 을 선언하고 넣어준다.
그리고 for 문을 사용해 a 에 num 의 자릿수를 찾아준다.
for 문 대신 while 문을 사용해도 됨.
그리고 자릿수 만큼의 값을, 새로운 uint[] 에 지정해준다.
자릿수를 지정하지 않으면, new uint[] 문법을 사용할 수 없기 때문이다.
그리고 다시 for 또는 while 문을 이용해서 number 배열에 % 로 나눈 나머지값을 하나하나 넣어준다.
만약, 3579 라는 값이라면 for 문 안에서 이렇게 배열에 하나 하나 값이 담길 것이다.
number[3] = 3579 % 10; -> 9
number[2] = 357 % 10; -> 7
number[1] = 35 % 10; -> 5
number[0] = 3 % 10; -> 3
이렇게 값을 담은 배열의 길이와 값을 반환해주면 된다.
2) 문자열을 넣었을 때 그 문자열의 자릿수와 문자열을 한 글자씩 분리한 결과를 반환하세요. 예시) bear > 4 / b,e,a,r
아래와 같이 함수를 만들었다.
function test2(string memory _n) public pure returns(uint, string[] memory, string memory){
uint length;
length = bytes(_n).length;
bytes1[] memory array = new bytes1[](length);
for(uint i; i < length ; i++){
array[i] = bytes(_n)[i];
}
string[] memory str = new string[](length);
for(uint i; i < length ; i++){
str[i] = string(abi.encodePacked(array[i]));
}
return (length, str, string(abi.encodePacked(array)));
}
먼저, string 형태는 숫자보다 더 쉽다고 생각했다. string => bytes 형태로 바로 바꿀 수 있기에.
먼저, 받아온 string을 bytes으로 바꾸어주었다.
bytes 는 배열이기에, length 로 배열의 길이를 가져올 수 있다.
length 라는 지역변수를 선언해서 배열의 길이 값을 넣어주었다.
그리고 for 문을 이용해서 bytes(_n)[] 을 하나하나 array 에 넣어주었고,
그리고 new 를 이용해 length 만큼의 자릿수를 정의해준 string[] 형태 str 을 선언해주었다.
그리고 for 문을 이용해서 str 배열에 string 으로 변환시킨 bytes 값을 하나하나 넣어주었다.
이 문제에서 가장 막혔던 부분은... 아래다.
string(abi.encodePacked(array[]))
이전까진 keccak256 만 사용했지, abi encodedpacked 을 사용해서 bytes => string 으로 형변환 시키는 방법은 모르고 있었다.
string(array[])
이렇게도 만들어보고 여러가지 방법을 다 시도해봤었는데, 결국 저 마지막 부분은 답지를 보고 풀었다.