local variable, global variable

변수는 함수내에서 선언이 되면 해당 함수가 호출 시에 생성이 되고 함수가 종료되면 소멸된다. 이러한 변수를 지역변수라고 한다. 함수 밖에서 선언이 된 변수는 전역변수라고 부르며 프로그램이 실행되면 생성이 되며 프로그램이 종료되어야 소멸한다. 

var a = 10      //전역변수  -> 함수 내외에서 호출 가능
var b = 20      //전역변수

func fn_1(){
    var c = 3000    //지역변수 -> 해당함수 내에서만 호출 가능, 함수 종료시 소멸.
    print("\t fn_1()시작 a:\(a), b:\(b), c:\(c)")
    a = 11  //전역변수 a 변경
    b = 21  //전역변수 a 변경
    print("\t fn_1()끝 a:\(a), b:\(b), c:\(c)")
}

func fn_2(){
    var d = 4000    //지역변수 -> 해당함수 내에서만 호출 가능, 함수 종료시 소멸.
    print("\t fn_2()시작 a:\(a), b:\(b), d:\(d)") //전역변수 b호출
    a=13    //전역변수 a 변경
    var b = 23  //지역변수 b 선언 -> 우선순위가 전역변수보다 높다.
    print("\t fn_2()끝 a:\(a), b:\(b), d:\(d)")  //지역변수 b 호출
}

func fnTot(){
    var e = 5432
    print("fnTot()시작 a:\(a), b:\(b), e:\(e)")
    fn_1()      //fn_1() 함수를 호출했다 하더라도, fn_1()의 지역변수를 호출할수는 없다.
    print("fnTot()중간 a:\(a), b:\(b), e:\(e)")
    a=12        //전역변수 a 변경
    fn_2()      //fn_2() 함수를 호출했다 하더라도, fn_2()의 지역변수를 호출할수는 없다.
    print("fnTot()끝 a:\(a), b:\(b), e:\(e)")
}

fnTot()

지역변수의 특징은 선언된 함수 내에서만 접근이 가능하다는 점이다. 다른 함수가 호출하였다 하더라도 해당 지역변수에는 접근할 수 없으며, 반대로 전역변수의 경우 어디서든 접근이 가능한 것이 특징이다.


selfCall

selfCall(재귀함수)는 자기 자신을 호출하는 함수를 뜻한다. 반복문과 마찬가지로 탈출조건을 적지 않으면 무한루프에 빠지게 되니 주의깊게 작성해야 한다. 

//재귀함수를 이용하여 0부터 매개변수까지의 합 구하기
var pre = ""
func fn(no:Int) -> Int{
    let myPre = pre
    pre += "\t"
    
    var res = no
    
    print("\(myPre)fn() 시작:\(no), \(res)")
    
    if no > 0 {     //조건
//        let rr = fn(no:no-1) //증감 : -1
//        res += rr
//        print(no, rr, res)
        res += fn(no:no-1)
    }
    
    print("\(myPre)fn() 끝:\(no), \(res)")
    return res
}

var qq = fn(no:3)    //초기값
print("main: \(qq)")

pre변수는 함수의 출력부분을 편하게 구분하기 위해 출력한 것이므로 주의깊게 보지 않아도 된다. fn(3)의 호출 흐름을 간단하게 그려보았다.


class

클래스는 표현하고자 하는 것을 자료형으로 정의한 것이라고 생각하면 된다. 

표현하고자 하는 것의 속성 및 상태를 변수로 표현하고, 그것의 동작을 함수로 표현한다. 이때 클래스에 정의한 변수를 멤버변수, 클래스에 정의한 함수를 메소드라고 지칭한다. 클래스를 정의하고 사용하기까지 4단계를 거친다.

  1. 클래스 정의
  2. 클래스 형의 변수의 선언
  3. 생성잘르 통해 생성 (heap영역에 생성)
  4. 인스턴스 클래스의 호출

클래스 정의

클래스를 정의한다. 멤버변수를 통해 표현하고자 하는 것의 속성과 특징을 서술하고, 메소드를 통해 동작을 표현한다.

class AAA {
    var aa = 10     //멤버변수: 상태
    var tt = "아기상어"     //멤버변수
    
    func fn_1(){        //메소드: 동작
        print("\(aa), \(tt) AAA fn_1() 실행") //해당 클래스의 멤버요소 접근 가능
    }
    
    func fn_2(){        //메소드
        print("AAA fn_2() 실행")
    }
}

클래스형 변수 선언

클래스를 실체화하여 사용하기 위해 변수로 선언한다. 

var a1:AAA  //정의된 클래스를 통해 변수를 선언한다.
var a2:AAA //물론 여러개를 만들 수 있다.

생성

선언한 변수를 사용하기 위해 생성자로 초기화한다.

a1=AAA()    //생성 및 대입
a2=AAA()

인스턴트 클래스 호출 및 접근

변수를 통해 메소드 호출 및 멤버변수에 접근 한다.

print(a1)   //인스턴스 변수 호출 ::> 클래스 타입을 알 수 있다.

print(a1.aa,a1.tt)    //멤버변수 호출
print(a2.aa,a2.tt)    //멤버변수 호출

a1.fn_1()	//메소드 호출
a2.fn_1()	//메소드 호출

a1.aa = 123     //멤버변수에 값 대입
a1.tt = "아빠상어"  //멤버변수에 값 대입

print("a1.aa:",a1.aa,"/ a1.tt:",a1.tt)    //멤버변수 호출
print("a2.aa:",a2.aa,"/ a2.tt:",a2.tt)    //멤버변수 호출

print("-----------------")

a1.fn_1()   //메소드 호출
a1.fn_2()   //메소드 호출

a2.fn_1()
a2.fn_2()

실행 결과를 보면 멤버변수는 각 독립된 존재인 것을 알 수 있다.


클래스 예제

클래스를 통해 입력받은 점수로 합계와 평균을 구하는 클래스를 생성해보았다.

class Exam{     //클래스명은 대문자로 시작한다.
    var name = ""
    var score = [Int]()
    var tot = 0, avg = 0
    
    func first(_ nn:String, _ jj:Int...){
        //1.입력부
        self.name = nn
        self.score = jj
        
        //2.계산부
        self.calc()
        
        //3.출력부
        self.ppp()
    }
    
    func calc(){
        tot = 0
        for i in score{
            tot += i
        }
        avg = tot / score.count
    }
    
    func ppp(){
        print("\(name)\t\(score)\t총점:\(tot)\t평균:\(avg)")
    }
}

비중있게 보아야 할 부분이라면 first()함수에서 매개변수의 값을 멤버변수에 옮겨 담은 후, calc()함수와 ppp()함수를 호출하여 한번에 입력, 계산, 출력을 동작하게 하는 부분이다. 코드가 짧다면 굳이 함수를 나눌 필요없이 first()함수 안에 다른 코드들을 적으면 되지만, 함수의 내용이 커지면 예제와 같이 분리하여 작성하는 것이 분업, 코드의 유지보수에도 더 편리하다. 

var st1 = Exam()	//Exam()형태의 변수 생성
var st2 = Exam()

st1.first("한가인", 77,78,71)  //입력과 동시에 출력.
st2.first("두가인", 87,88,81)

'iOS > TJ' 카테고리의 다른 글

day13_initializer, self, inheritance  (0) 2021.07.01
day11_param, return, funcCall  (0) 2021.06.18
day10_func()  (0) 2021.06.17
day09_dictionary, set  (0) 2021.06.16
day08_multiArray, tuple  (0) 2021.06.03

gcp로 운영 중이던 웹페이지가 db를 읽어오지 못하는 현상이 생겼다. 아래는 웹페이지에서 볼 수 있는 문구.

마지막 따옴표 사이에 읽어와야할 db명이 적혀있다.

java.sql.SQLSyntaxErrorException: Could not connect to address=(host=127.0.0.1)(port=3306)(type=master) : Unknown database 'wifi '

문제를 파악하기 위해 서버로 접근하여 확인해본 결과 db가 없어졌다. 

sql파일을 가지고 있어서 다시 올리면 그만인데 문제는 며칠 간격으로 db가 계속 사라진다는 것.. 

처음 웹페이지를 운영해본터라 서버 문제인 줄 알고 로그는 확인도 안 하고 반복해서 sql만 새로 올렸다.

반복되는 오류에 뒤늦게 로그를 확인해봐야겠다는 생각이 들었다. 문제는 mariaDB만 그런 건지 db를 올리면 기본값이 로그를 남기지 않게끔 되어있었다. 그래서 이번에는 로그를 남기게 설정해놓고 주기적으로 들어가서 확인해 보았다. 

 

결론

 새벽 4시경에 누군가 db에 접속하여 자료를 건드는 로그를 발견.

지워진 당일날 확인하니 db자체는 남아있지만 안에 있는 테이블과 자료들은 없다. readme를 읽어보니 데이터를 복구하고싶으면 토르 브라우저로 접속하여 비트코인을 보내란다.. 즉 랜섬웨어다. 

처음에는 기존 db도 안남겨놓고 readme만 있었는데, 나는 readme가 mariaDB에서 작성된 기본 db로 착각해서 열어볼 생각을 안 했었다.

돈 안 보내고 쭉 지켜봤더니 이번에는 내 데이터는 백업되어 있단다.. 48시간 내로 돈 안 주면 유출할 거라는 문구로 변경되어 있다. 나름 신기한 경험을 해보았고 로그의 중요성을 깨달았다. 로그 볼 생각 안 했으면 계속 반복해서 sql만 올렸을 테니.. 해당 gcp서버는 파기 후 다시 가동하면 그만이다. 다만 걱정되는 건 어떤 경로로 감염이 된 건지를 잘 모르겠다.

'web > errors' 카테고리의 다른 글

서버 재부팅 후 도메인 연결 안되는 문제  (0) 2021.06.10

parameter

함수의 호출 시 parameter(매개변수)는 모두 적어야한다. 하지만 함수를 정의할 때 매개변수의 기본값을 명시해 놓으면 호출시에 매개변수를 명시하지 않아도 기본값으로 대입된다. 

func fn_1 (name:String, age:Int, marriage:Bool){
    print("name:\(name), age:\(age), marriage: \(marriage)")
}

func fn_2 (name:String, age:Int=21, marriage:Bool = false){    //marriage, age에 기본값 설정
    print("name:\(name), age:\(age), marriage: \(marriage)")
}

fn_1(name:"차은우", age:34, marriage: false)//기본값을 명시하지 않아 인수를 모두 명시해야한다.
fn_2(name:"차은수", age:24)    //기본값이 입력되어있는 매개변수는 인수를 작성하지 않아도 된다.
fn_2(name:"차은미")    //기본값이 입력되어있는 매개변수는 인수를 작성하지 않아도 된다.

fn_1()과 달리 fn_2()는 매개변수에 기본 값이 설정되어 있기에 호출 시 생략이 가능하다.

func exam(name:String, type:String = "일반", kor:Int, eng:Int, art:Int){
    var res = 0
    if type == "일반"{	//type이 일반일 경우와 아닐경우를 나누어서 점수를 계산한다.
        res = (kor + eng + art)/3
    } else {
        res = Int((Double(kor)*0.3) + (Double(eng)*0.2) + (Double(art)*0.5))
    }
    
    print("\(type) \(name) : \(res)")
}

exam(name: "차금우", kor: 87, eng: 90, art: 30)    //기본값인 일반으로 계산
exam(name: "차동우", type: "특기생", kor: 87, eng: 90, art: 30)   //일반이 아닌 다른 경우로 계산

이어서 매개변수에는 가변매개변수가 있다. 자료형 뒤에 ...을 붙여 명시하며 이는 호출 시 연속하여 자료를 입력받고, 이를 배열로 처리한다. 호출시에 배열형태로 입력을 받으면 에러가 일어난다는 점을 기억하자.

func fn_3(id:String, size:[Int]){   //매개변수를 배열로 받음
    print("id: \(id), size: \(size)")
}

func fn_4(id:String, size:Int...){  //정수형 가변매개변수
    print("id: \(id), size: \(size)")
}

fn_3(id:"aaa", size: [5,7,2,3,9])
//fn_3(id:"aaa", size: 5,7,2,3,9)   //배열형태가 아니어서 에러


fn_4(id:"ccc", size: 5,7,2,3,9)     //입력 정수들을 알아서 배열로 묶어준다. 매개변수의 갯수에 따라 다른 연산도 가능하다.
//fn_4(id:"ccc", size: [5,7,2,3,9])   배열형태면 에러.


가변매개변수 예제

가변매개변수를 사용하여 각 다른 과목수의 평균, 합계를 구한다.

//Q.숫자들을 입력하여 총점, 평균을 출력하는 함수를 구현하세요.
func res(_ score:Int...){
    var result = 0
    
    for i in score{
        result += i
    }
    
    print("총점:",result, "평균:",result/score.count)
}
res(67,62,66,65,68)
res(77,75,73)


return

함수는 크게 4가지 종류로 나뉜다.

  • 매개변수와 return이 있는 함수
  • 매개변수는 있지만 return이 없는 함수
  • 매개변수가 없지만 return이 있는 함수
  • 둘 다 없는 함수 

여기서 return에 대해서 이야기 해본다면 return이 없는 함수의 경우 4가지 방법으로 정의할 수 있다.

func fn_1(){
    print("fn_1 () 실행 1")
    print("fn_1 () 실행 2")
    print("fn_1 () 실행 3")
    print()
}

func fn_2(){
    print("fn_2 () 실행 1")
    print("fn_2 () 실행 2")
    print("fn_2 () 실행 3")
    print()
    return;     //reutrn;은 항상 생략되어있다.
}

func fn_3() -> Void{    //return이 없다는 뜻이다.
    print("fn_3 () 실행 1")
    print("fn_3 () 실행 2")
    print("fn_3 () 실행 3")
    print()
    return;     //reutrn;은 항상 생략되어있다.
}

func fn_4() -> (){    //return이 없다는 뜻이다.
    print("fn_4 () 실행 1")
    print("fn_4 () 실행 2")
    print("fn_4 () 실행 3")
    print()
    return;     //reutrn;은 항상 생략되어있다.
}


fn_1()
fn_2()
fn_3()
fn_4()

return; 문구는 별다른 반환없이 종료한다는 얘기이다. 그렇다면 return; 문구는 함수 중간에 적어보겠다.

func fn_5(){    //return이 없다는 뜻이다.
    print("fn_5 () 실행 1")
    print()
    return;     //여기서 함수가 종료된다. 즉 return은 함수의 탈출을 뜻한다.
    print("fn_5 () 실행 2")
    print()
    return;
    print("fn_5 () 실행 3")
    print()
    return;
}

fn_5()	//함수실행

실행1부분만 출력되고 그 이후는 함수가 종료된다. 즉 return; 은 함수의 탈출을 의미한다.

func fn_6(_ num : Int){
    if num >= 88{
        print("fn_6(): 우수")
        print()
        return;
    }
    
    if num >= 60{
        print("fn_6(): 정상")
        print()
        return;
    }
    
    print("fn_6(): 미달")
    print()
    return;
}
fn_6(59)    //미달
fn_6(89)    //우수
fn_6(66)    //정상

이런식으로 조건문을 사용하여 함수의 종료시점을 정해줄 수 있다는 것이다.


일반적으로 return은 하나의 자료만 반환이 가능하다. 그렇다면 여러 자료를 반환하려면 어떻게 하면될까?

func fn_7() -> Int {
    print("fn_7() : 실행")
    return 100
}

func fn_8() -> [Int] {
    print("fn_8() : 실행")
    return [100,200]
}

func fn_9() -> (String,Int) {
    print("fn_9() : 실행")
    return ("아기상어",200)
}

var rr = fn_7()
print("rr: \(rr)"); print()

var rr2 = fn_8()
print("rr2: \(rr2)"); print()

var rr3 = fn_9()
print("rr3: \(rr3)"); print()

//튜플을 리턴받아 대입에 사용할 수 있다.
var ss:String
var ii:Int
(ss,ii) = fn_9()
print("ss:\(ss), ii:\(ii)"); print()

배열이나 튜플과 같이 시퀀스자료를 반환하면 여러자료를 반환할 수 있고, 이러한 반환을 대입에도 사용할 수 있다.


return 예제1

가변매개변수로 연속하여 자료를 받고, 해당 매개변수에서 가장 큰 값과 작은 값을 리턴하는 함수이다.

typealias numType = (max:Int, min:Int)
func max_min(_ nums:Int...) -> numType{     //typealias로 묶어서 반환이 가능하다.
    
    if nums.count == 0{     //빈배열이 들어온다면?
        print("연산불가")
        return(0,0);
    }
    
    var max = nums[0]   //첫번째 수로 초기화 -> 처음에 비교할 기준이 필요하기 때문
    var min = nums[0]   //첫번째 수로 초기화
    
    for i in nums{	//대소비교 시작
        if max < i {
            max = i
        }
        if min > i {
            min = i
        }
    }
    
    return (max, min)	//numType으로 리턴
}

var ret = max_min(67,32,98,2,15,65,3,76)
print("최대: \(ret.max)")
print("최소: \(ret.min)");
print("--------------------------")

var rrr = max_min()
print("최대: \(rrr.max)")
print("최소: \(rrr.min)")


return 예제2

num의 배수에 해당하는 숫자들을 리턴하는 함수를 구현하세요
mulArr(num:3, 45,56,67,78,76,12,34,26)

func mulArr(num:Int, _ nums: Int...) -> [Int]{
    var res = [Int]()
    
    for i in nums{
        if i%num == 0{  //배열속의 숫자가 num으로 나누어지면 res배열에 추가.
            res.append(i)
        }
    }
    
    print("\(num)의 배수는 \(res)입니다.")
    return res;
}
print(mulArr(num:3, 45,56,67,78,76,12,34,26))


funcCall

함수는 다른 함수를 호출할 수 있다.

func fnTot(){
    var aa = 10
    print("fnTot() 실행 1 \(aa)")
    fn_1()      //함수 내에서 함수를 호출한다.
    print("fnTot() 실행 2 \(aa)") //여기서는 fn_1()의 bb를 호출할 수 없다.
    fn_2()      //함수 내에서 함수를 호출한다.
    print("fnTot() 실행 3 \(aa)")
}

func fn_1(){
    var bb = 20
    print("fn_1() 실행 \(bb)")  //여기선 fnTot()의 aa를 호출할 수 없다.
}
func fn_2(){
    print("fn_2() 실행 ")
}

fnTot()	//fnTot()호출

여기서 알아두어야 할 점은 fnTot()에서 fn_1()을 호출했다 하더라도 fnTot()에서는 fn_1()에서 선언된 변수 bb에 접근 할 수 없다는 것이다. 함수는 각각 정해진 영역을 가지고 동작하며, 이러한 특성을 생각해서 변수의 지정과 선언을 잘해야한다.

'iOS > TJ' 카테고리의 다른 글

day13_initializer, self, inheritance  (0) 2021.07.01
day12_funcVariable, selfCall, class  (0) 2021.06.29
day10_func()  (0) 2021.06.17
day09_dictionary, set  (0) 2021.06.16
day08_multiArray, tuple  (0) 2021.06.03

function

함수(function)는 프로그램의 가장 작은 단위이자 하나의 작은 프로그램이다. 함수를 사용하는 데에는 크게 두가지 이유가 있다. 첫번째는 작업단위의 모듈화. 두번째는 재사용의 용이성이다. 

함수는 미리 정의한 뒤, 호출을 통해서 사용할 수 있다. 다음은 함수의 정의 표현이다.

     func 함수이름 (매개변수:자료형 ... ) -> 리턴타입 {

          실행구문

          return 반환값

     }

매개변수에는 자료형을 명시해야하며 함수에 매개변수, 리턴타입이 없다면 생략이 가능하다. 다음은 함수의 호출형식이다.

     함수명(인수1, 인수2, ... )

     변수 = 함수명(인수1, 인수2, ... )

함수의 호출 시 인수의 순서는 바뀌면 안되며 반드시 함수의 매개변수에 맞게 인수를 적어야한다.

//함수정의
func fn_1(aa:Int, bnbn bb:String) -> Int{
    print("fn_1() 실행1 \(aa)")   //함수실행코드
    print("fn_1() 실행2 \(bb)")   //매개변수는 지역변수로 활용된다.
    print("fn_1() 실행3")
    
    return 1234 //리턴
}

var rr = fn_1(aa:10, bnbn:"아기상어")	//함수호출
print("rr: \(rr)")  //리턴 값 확인

fn_1()함수에 매개변수를 보면 bnbn bb : String이라 되어있다. 스위프트의 함수가 다른언어들과의 차이점이 매개변수와 인수를 따로 구분한다는 점이다. bnbn은 인수, 즉 외부에서 호출 시의 매개변수를 부르는 이름이다. 반대로 함수 내부에서는 bnbn이 아닌 bb로 사용해야 매개변수로서 접근이 가능하다. 코딩시 영어문장처럼 흘러가도록 하기위해 이와 같이 매개변수와 인수를 따로 구분하는 것으로 알고있다. 해당 문법이 불편하면 매개변수만 적게되으면 된다. 인수와 매개변수를 통일하여 사용 할 수 있다.

fn_1(aa: 20, bnbn: "아빠상어"); 
fn_1(aa: 678, bnbn: "엄마상어")
fn_1(aa: 1920, bnbn: "할머니상어")

이와 같이 함수를 사용하게 되면 반복작업을 손쉽게 처리할 수 있다.


label 생략

label(인수)은 생략이 가능하다. 인수명을 언더바로 적으면 호출 시에 인수명을 따로 적지 않고 바로 입력하여 함수를 호출 할 수 있다.

func fn_1(name:String, age:Int, marriage:Bool){
    print("fn_1() name:\(name), age:\(age), marriage:\(marriage)")
}

func fn_2(_ name:String, aa age:Int, mm marriage:Bool){
    print("fn_2() name:\(name), age:\(age), marriage:\(marriage)")
}

func fn_3(_ name:String, _ age:Int, _ marriage:Bool){
    print("fn_3() name:\(name), age:\(age), marriage:\(marriage)")
}

//fn_1(name: "정우성", marriage: false, age: 51) //인수의 순서는 바꾸면 안된다.
fn_1(name: "정우성", age: 51, marriage: false) //스위프트의 함수는 호출 시 인수(label)를 적어야 한다.
fn_2("정좌성", aa:51, mm:false)    //인수(label)를 언더바(_)로 생략처리하면 인수를 적으면 안된다.
fn_3("정남성", 51, false)


func 예제

정수형 배열을 매개변수로 받아 그 중 가장 작은 값을 반환하는 함수를 작성해보았다.

func minGo(arr:[Int]) -> Int{
    var res = arr[0]	//매개변수로 배열의 첫번째 요소를 res변수에 담는다. 
    
    for i in arr{
        if res > i {
            res = i	//res의 값보다 더 작은 값의 요소를 찾으면 res에 대입한다.
        }
    }
    return res		//res값을 반환한다.
}

var res = minGo(arr: [34,56,7,12,4,98,23,167,11])
print("res:",res)

'iOS > TJ' 카테고리의 다른 글

day12_funcVariable, selfCall, class  (0) 2021.06.29
day11_param, return, funcCall  (0) 2021.06.18
day09_dictionary, set  (0) 2021.06.16
day08_multiArray, tuple  (0) 2021.06.03
day07_array  (0) 2021.06.02

Dictionary

딕셔너리는 배열, 튜플과 달리 순서없는 데이터의 모음이다. 단, 데이터는 '키-값'의 한쌍으로 구분된다. 

따라서 딕셔너리의 선언은 키의 자료형과 값의 자료형을 명시하여 선언한다.

var 딕셔너리이름 : Dictionary< 키자료형 : 값자료형 > = [키1:값, 키2:값, 키3:값, ... ]

값은 중복할 수 있으나, 키는 값을 호출하는데에 사용하기에 중첩할 수 없다.

//선언 및 생성
var dic:Dictionary<String:String> = ["red":"빨강","blue":"파랑","green":"파랑","yellow":"노랑"]    
//키는 중첩되면 안된다.
print(dic)  //딕셔너리는 순서가 랜덤하게 출력된다.

딕셔너리의 출력 결과를 보면 순서없이 출력되는 모습을 볼 수 있다.


선언 및 초기화

선언 및 초기화에는 여러가지 방법들이 있다.

//선언 및 비어있는 형태로 생성
var dic2:[String:Int] = [:]     //String형태의 키, Int형태의 값으로 생성.
print(dic2)

var dic3:Dictionary<Int,Int> = [:]  //Int형태의 키, Int형태의 값으로 생성.
print(dic3)

dic3 = [3:100,7:200,10:5]   //키-값 대입하여 초기화
print(dic3)

var dic4 = [String:String]()    //비어있는 형태로 생성
print(dic4)

var dic5 = Dictionary<Int,String>() //비어있는 형태로 생성
print(dic5)


원소 호출

딕셔너리의 원소(값)의 호출방법이다. 키를 이용하여 호출 및 접근한다.

//원소호출
//print(dic[0]) 딕셔너리는 순서가 없다.
print("dic[\"red\"]:",dic["red"]!)  //옵셔널로 반환된다. 값이 없을수도 있기에
print("dic[\"pink\"]:",dic["pink"])  //없는 키를 출력시 nil이 출력된다.(옵셔널)

기본적으로 출력 형태는 옵셔널로 출력된다. 키와 값의 존재유무가 확실하지 않기 때문이다.


추가 및 대입

딕셔너리는 튜플과 달리 원소의 추가가 가능하며, 기존의 키에 해당하는 값의 변경 또한 가능하다.

//대입 및 추가
dic["yellow"] = "누렁이"   //기존의 키에 새로운 값 대입
dic["orange"] = "주황"    //'키-값'의 추가도 가능하다.
print(dic)


삭제

대입 및 추가와 같이 키를 사용하여 값을 삭제할 수 있다.

//삭제
print(dic)	//기존 배열 출력
dic.removeValue(forKey: "green")    //green 키-값 삭제
print(dic)	//삭제된 배열 출력


출력

딕셔너리또한 반복문과 사용하면 편리하게 출력 및 제어할 수 있다.

//개별 출력
for i in dic {
    print(i)    //튜플형태로 출력된다.
    print(i.key, i.value)	//키와 값을 따로 출력할 수 있다.
}
print("------------\n")

for (k, v) in dic{
    print(k,v)	//키와 값을 더 간략하게 출력할 수 있는 방법이다.
}

var k = dic.keys    //키만 따로 뽑아낼 수 있다.
print(k)    //배열형태로 출력된다.

var v = dic.values //값만 따로 뽑아낼 수 있다.
print(v)    //배열형태로 출력된다.


메소드

딕셔너리의 여러가지 메소드를 통해 데이터를 반환받을 수 있다.

//비어있는 딕셔너리인지 확인
print(dic.isEmpty)

//딕셔너리의 갯수 출력
print(dic.count)

//딕셔너리 비우기
dic.removeAll()
print(dic.isEmpty)
print(dic.count)


Dictionary 예제

중복된 숫자의 배열을 받아서 각 숫자가 몇번 중복되었는지 딕셔너리형태로 출력해보자.

let hit = [3,4,7,8,1,3,5,7,1,3,4,8,1,1] //안타 친 등번호 순서
var hitData = [Int:Int]()   //비어있는 딕셔너리 생성

for i in hit{
    var no = 1
//    print(i, hitData[i]==nil)   //해당 데이터가 중복인지 확인.
    
    if hitData[i] != nil{   //중복된 자료라면,
        no += hitData[i]!   //hitData[i]의 값을 1증가한다.
    }
    
    hitData[i] = no  //중복이 되지 않으면서 대입이 된다.
}
print(hit)	//기존 데이터 출력
print(hitData)  //[등번호 : 안타횟수] 형태로 출력

[번호 : 중복횟수] 형태로 출력이 되지만, 딕셔너리는 순서없는 자료형이기에, 랜덤하게 출력이 된다. 오름차순으로 정렬하려면 따로 가공하여 출력해야한다.

//dictionary는 순서없는 자료형이기에, sort()함수는 불가능. sorted()함수를 이용하여 정렬된 자료를 반환받아 처리해야한다.
for k in hitData.keys.sorted(){
    print("\(k):\(hitData[k]!)개","",separator: "\t",terminator: "")   //등번호, 안타횟수 출력
}

최종출력은 다음과 같다. print()함수의 separactor 속성은 다음 데이터 출력 시 기존 데이터와 구분하기 위해 출력내용을 정해주는 속성이다. 한번에 데이터가 두개 이상 출력이 되어야 적용되므로 기존 자료와 공백 데이터("")를 출력하게 작성했다.


Set

딕셔너리는 키와 값으로 이루어진 순서없는 데이터의 모음(컬렉션 타입)이다. 하지만 키-값 쌍의 형태가 아닌 단일형태의 순서없는 데이터 모음이 필요한 경우 Set을 사용한다. 즉 키로만 이루어진 딕셔너리라고 생각하면 된다.

var 변수이름 : Set<자료형> = [자료,자료,자료]

var arr = [11,22,11,33,22,44,11,55]     //배열    중복데이터가 허용된다. 순서가 있다.
var ss1:Set<Int> = [11,22,11,33,22,44,11,55]    //set 중복데이터는 생략되어 저장된다. 순서가 없다.

print(arr)
print(ss1)

값으로 이루어진 딕셔너리가 아닌 키로만 이루어진 모임이라고 설명한 것은 바로 데이터의 중복이 허용되지 않는다는 점 때문이다. 일반적인 배열과 큰 차이이다. 출력 모습도 배열은 인덱스 순서로 출력되는 반면, set은 순서없이 출력이 되는 모습을 볼 수 있다.


선언 및 초기화

var ss2 = Set<String>()     //빈 set생성
var ss3:Set<Int> = []       //빈 set생성

print(ss2)
print(ss3)

ss2 = ["a","b","d"]     //빈 set 초기화
ss3 = [3,4,4,5,6,7]     //빈 set 초기화

print(ss2)
print(ss3)


요소 갯수 출력

var ss1:Set<Int> = [11,22,11,33,22,44,11,55]

//set 요소 갯수출력
print(ss1.count)

중복이 허용되지 않아 ss1의 요소는 5개로 출력한다.


반복문 출력

var ss1:Set<Int> = [11,22,11,33,22,44,11,55]

//반복문 출력
for i in ss1 {
    print(i)    //원소를 하나씩 가지고 올 수 없다. 순서가 없기 때문이다.
}

Set은 배열과 달리 인덱스가 없기에 반복문을 통해 요소를 하나씩 출력할 수 없다. 


요소 검색, 추가, 삭제

var ss1:Set<Int> = [11,22,11,33,22,44,11,55]

//요소 검색
print(ss1.contains(33))	//요소 33이 있는가?
print(ss1.contains(100))//요소 100이 있는가?

//요소 추가
ss1.insert(77)  //요소 77 추가 *중복데이터를 추가하면 변화가 일어나지 않는다.
print(ss1)

//요소 삭제
ss1.remove(22)  //요소 22 삭제
print(ss1)


isEmpty, 전체삭제

var ss1:Set<Int> = [11,22,11,33,22,44,11,55]

//빈 set인지 확인
print(ss1.isEmpty)  //bool return

//요소 전체삭제
ss1.removeAll() //요소 전체삭제
print(ss1.isEmpty)

isEmpty메소드를 통해 비어있는 배열인지 확인 후 bool값을 반환해준다.


set 집합연산

set에는 여러가지 집합연산이 메소드 형태로 존재한다.

var s1 : Set<Int> = [1,2,3,4,5,6]   //순서없는 자료 묶음
var s2 : Set<Int> = [4,5,6,7,8,9]

print("s1 \(s1)")
print("s2 \(s2)")

var rr = s1.union(s2)   //합집합
print("s1.union(s2) \(rr)")

rr = s1.intersection(s2)    //교집합
print("s1.intersection(s2) \(rr)")

rr = s1.symmetricDifference(s2) //합집합 - 교집합
print("s1.symmetricDifference(s2) \(rr)")

rr = s1.subtracting(s2) //s1 차집합 s2
print("s1.subtracting(s2) \(rr)")

rr = s2.subtracting(s1) //s2 차집합 s1
print("s2.subtracting(s1) \(rr)")


set 예제1

set을 사용하여 로또 번호 생성기를 작성해보자.

var lotto1 = [Int]()
var lotto2 = Set<Int>()

while true {
    let no = Int.random(in: 1...45)	//1부터 45까지 랜덤번호 생성 메소드
    
    lotto1.append(no)   //배열에 요소 추가
    lotto2.insert(no)   //set에 요소 추가
    
    if lotto2.count == 7 {  //set의 요소가 7개가 되면 종료
        break
    }
}

print("lotto1: \(lotto1)")  //배열의 경우 겹치는 수가 존재한다.
print("lotto2: \(lotto2)")  //set의 경우 겹치는 수가 없다.

배열과의 비교를 통해 두개를 작성했다. 배열의 경우 중복이 허용되기 때문에 중복된 숫자가 생성된다. 하지만 set의 경우 중복을 허용하지 않기에 요소가 7개가 될 때 반복문을 탈출하면 된다.


set 예제2

set을 이용하여 빙고판을 만들어 보자.

var number = Set<Int>()
var cnt = 0

while number.count < 25{    //set의 요소가 25가 될때까지 랜덤숫자 삽입
    number.insert(Int.random(in: 1...100))
}

for i in number{
    cnt += 1
    print(i,terminator: "\t")   //줄 변경 없이 출력
    if cnt % 5 == 0 {   //cnt가 5의 배수가 되면 줄 변경
        print()
    }
}

set의 중복 불허용의 특성을 이용하여 5x5 숫자빙고를 출력하였다. 


set 예제3

각 리스트의 중복을 제거하는 예제이다. 

let first:Set<String> = ["박재상","박정권", "최정", "김광현", "엄정욱", "박희수", "이호준"]
let second:Set<String> = ["이호준", "엄정욱", "박재홍", "이신협", "장동건"]
let fa:Set<String> = ["이병규", "이승엽", "박정권", "장동건", "박용택", "홍성흔"]

print("변경 전---------------------------")
print("1군 리스트:",first)
print("2군 리스트:",second)
print("FA 리스트:",fa)

let afterFirst = first.subtracting(second).subtracting(fa)  //1군 - 2군 - FA
let afterSecond = second.subtracting(fa)	//2군 - FA
let afterFa = fa.subtracting(first).subtracting(second) //FA - 1군 - 2군

print("변경 후---------------------------")
print("1군 리스트:",afterFirst)
print("2군 리스트:",afterSecond)
print("FA가능 리스트:",afterFa)

야구를 안봐서 뭐하는 예제인지는 모르겠다. 

'iOS > TJ' 카테고리의 다른 글

day11_param, return, funcCall  (0) 2021.06.18
day10_func()  (0) 2021.06.17
day08_multiArray, tuple  (0) 2021.06.03
day07_array  (0) 2021.06.02
day06_control flow_loop  (0) 2021.05.27

구글 클라우드 플랫폼으로 서버를 가동 중, ssh으로 접속 시, [time out]으로 접근이 불가능한 상태였다.

간단하게 인스턴스 재부팅을 진행. ssh접속은 가능해졌으나, 이후에 문제가 발생했다. godaddy에서 구매한 도메인으로는 연결이 안 되는 현상.. 톰캣이 실행이 안되었나 확인해보니 외부 ip로는 접속이 잘된다...

 

이때부터 아무 잘못없는 고대디에 가서 도메인 초기화 -> 네임서버 재연결만 시도했었다. 여기서 또 삽질했던 것이 고대디가 미국 회사라 그런지 네임서버 바꾸는데도 에러.. 채팅으로 고대디 직원에게 직접 문의를 해서 변경을 부탁해야 적용이 되었는데, 문제는 아무리 연결을 해봐도 채팅 연결이 안 된다.. 무한 "chat Loading..." 열심히 구글링 해본 결과 접속 지역이 미국 이외의 경우라면 해당 증상이 생긴단다. -_- vpn으로 접속지역을 미국으로 바꾸니까 그제야 채팅 연결이 되고 초기화했던 네임서버를 재연결. 여기까지 오는 데에도 며칠 걸렸다. 그렇게 도메인 설정 초기화 후 네임서버까지 재연결 했으나, 여전히 등장한 'ERR_CONNECTION_REFUSED' 에러.

 

이후에 또 다시 헤맸는데, 결론은 기존의 80 포트를 8080으로 리다이렉팅 해놓았던 설정이 재부팅으로 인해 지워진 것이다. 이사실을 알고 난 뒤 도메인에 8080 포트로 접속해보니 잘된다. 즉 도메인 연결은 잘 되어 있었고 접속이 안 되는 포트로 접속하고 있었던 것... 

알고 나니 너무나 허탈했다. 다음에도 삽질하지 않기 위해 이 글을 남긴다.

 

iptables를 이용하여 80포트 8080으로 리다이렉팅

 

iptables -t nat -A PREROUTING -p tcp --dport 80 -j REDIRECT --to-port 8080

 

이후 잘 적용되었는지 확인하기 위한 명령어 

 

iptables -t nat -L

 

**재부팅돼도 초기화되지 않게 저장하는 명령어라는데,, 내 서버 환경에서는 해당 명령어가 수행되지 않는다.. 해결하기 전까지 재부팅하게 되면 80 포트 리다이렉팅을 까먹지 않도록 하자.**

service iptables save

----> 다행히도 빠르게 해결했다. service iptables save는 RHEL, Red Hat, CentOS에서 사용하는 명령어였고, 우분투에서는 다른 명령어로 접근을 해야 했다. 

sudo /sbin/iptables-save

 

이제 부터는 재부팅을 해도 80 포트 리다이렉팅 설정이 지워지지 않는다.

'web > errors' 카테고리의 다른 글

데이터 베이스가 주기적으로 삭제되는 현상  (2) 2021.06.25

2차원 배열

배열을 담는 배열이다. [자료형]이 배열이라면, [[자료형]]의 형태로 생성한다.

var st1 = [56,57,59]    // -> 자료형: [Int]
var st2 = [86,87,89]
var st3 = [76,77,79]
var st4 = [96,97,99]

print(st1)
print(st2)
print(st3)
print(st4)

var studs:[[Int]] = [st1,st2,st3,st4]   //[Int]의 배열형태. 즉, 배열들의 배열 -> 2차원배열
print(studs)

var score = [[77,79,71],[57,59,51,50],[97,99],st4,studs[0]]  // [[Int]]형 2차원 배열
print(score)		//2차원 배열 출력

for i in score{
    print(i)    	//2차원 배열의 요소에 접근. 즉 2차원 배열안의 배열들을 출력한다.
    for k in i {
        print(k)	//2차원 배열의 배열 속 요소 출력.
    }
}

print("score.count:",score.count)   		//2차원 배열의 요소갯수 -> 2차원 배열 속 '배열의 갯수'
print("score[0]:",score[0],score[0].count)	//2차원 배열의 0번째 배열접근, 0번째 배열의 원소갯수
print("score[1]:",score[1],score[1].count)	//2차원 배열의 1번째 배열접근, 1번째 배열의 원소갯수
print("score[2]:",score[2],score[2].count)	//2차원 배열의 2번째 배열접근, 2번째 배열의 원소갯수
print("score[3]:",score[3],score[3].count)	//2차원 배열의 3번째 배열접근, 3번째 배열의 원소갯수
print("score[4]:",score[4],score[4].count)	//2차원 배열의 4번째 배열접근, 4번째 배열의 원소갯수

2차원 배열도 배열에 관련된 함수를 사용할 수 있다. 접근을 몇 번하느냐에 따라 결과가 달라지는 모습을 볼 수 있다.

print("score[4]:",score[4],score[4].count)  			//4번째 배열접근, 4번째 배열의 원소갯수
print(score[4][0],score[4][1],score[4][2],separator: " ")	//2차원 배열 속 4번째 배열의 요소 출력

2차원 배열에 접근하게 되면 배열에 접근이 가능하고, 한번 더 접근하면 그 안의 요소에 접근이 가능하다.


2차원 배열 예제

2차원 배열을 이용한 예제다. 2차원 배열의 각 배열의 합, 평균, 원소의 평균을 구해보자.

var score = [[10,20,30], [300,400,500,600], [1,3,5,7,9]]
var total = 0
var cnt = 0

for i in score {
    var sum = 0
    
    for k in i{     //2차원 배열속 배열을 순회한다.
        sum += k    //각 배열속의 원소들의 합계를 구한다.
    }
    print(i, "합계:\(sum)", "평균:\(sum/i.count)")    
    //2차원 배열속 배열들 출력, 배열속 원소들의 합계, 배열속 원소들의 평균 출력
    total += sum   //합계들의 총합 계산(2차원배열의 총합)
    cnt += i.count  //원소들의 갯수 총합(2차원배열의 전체 평균을 얻기위함)
}

print(score, "배열합계:\(total)", "배열평균:\(total/score.count)", "원소평균:\(total/cnt)", "원소갯수:\(cnt)개")


Tuple

배열의 특징은 같은 자료형만 묶을 수 있다는 점이다. 이러한 점이 경우 따라서 편리하기도 하지만 반대로 다른 자료형과 함께 다룰 때에는 불편하게 작용한다. 이때 사용하는 것이 튜플이다. 튜플은 여러 자료형을 함께 묶을 수 있다.

var 튜플이름 : ( 자료형1, 자료형2, 자료형3, ... ) = ( 원소1, 원소2, 원소3, ...

단 선언 시에 지정된 자료형으로만 원소의 입력 및 변경이 가능하며, 원소의 추가 및 삭제는 할 수 없다. 

var t1:(String,Int,Bool) = ("원빈",174,true) //튜플 선언 및 초기화

print(t1)
print(t1.0)     //튜플에 접근할때는 점(.)으로 접근한다.
print(t1.1)
print(t1.2)

//t1.3 = 123.456    //다른 자료형으로 대입은 안된다.
t1.0 = "투빈"
print(t1)

var tt3:(name:String, height:Int, marriage:Bool) = ("현빈",188,false) //각 요소에 이름을 지정할 수 있다.

print(tt3)
print(tt3.name, tt3.0)      //각 요소에 이름으로 접근이 가능하다.
print(tt3.height, tt3.1)
print(tt3.marriage, tt3.2)


튜플 활용

튜플의 데이터를 다른 변수에 한번에 대입하는 작업도 가능하다.

var t2:(String,Int,Double) = ("젤리빈", 120, 987.432)
var nn:String
var hh:Int
var dd:Double

(nn,hh,dd) = t2     //튜플의 자료형에 맞게 여러 변수에 한번에 대입이 가능하다.

print(nn)
print(hh)
print(dd)


typealias

typealias를 사용하여 자료형을 미리 묶어놓고 사용할 수 있다.

typealias mem = (name:String, height:Int, marriage:Bool)    //여러 자료형을 mem이라는 자료형으로 묶었다.

var tt4:mem = ("김우빈", 183, true)    //mem 자료형으로 튜플을 생성한다.
var tt5:mem = ("커피빈", 163, false)    //mem 자료형으로 튜플을 생성한다.
var tt6:mem = ("미스터빈", 186, true)    //mem 자료형으로 튜플을 생성한다.

print(tt4)
print(tt5)
print(tt6)

var tts:[mem] = [tt4,tt5,tt6]   //typealias로 생성한 자료형으로 배열을 만들 수 있다.
print(tts)


튜플 예제

튜플을 이용한 예제이다. 배열속 튜플의 sum과 avg를 계산하여 대입하고, 등급을 계산 후 출력해보자.

typealias stud = (name:String, score:[Int], sum:Int, avg:Int, grade:String)

var exam:[stud] = [
    ("이효리",[77,78,71],0,0,""),
    ("삼효리",[57,58,51],0,0,""),
    ("사효리",[97,98,91],0,0,""),
    ("오효리",[67,68,61],0,0,""),
    ("육효리",[87,88,81],0,0,"")
]

//연산부
for i in 0..<exam.count{
    for k in exam[i].score{
        exam[i].sum += k        //i번째 튜플의 합 계산
    }
    exam[i].avg = exam[i].sum / exam[i].score.count //i번째 튜플의 평균 계산.
    
    if exam[i].avg >= 90 {
        exam[i].grade = "수"
    } else if exam[i].avg >= 80{
        exam[i].grade = "우"
    } else if exam[i].avg >= 70{
        exam[i].grade = "미"
    } else if exam[i].avg >= 60{
        exam[i].grade = "양"
    } else if exam[i].avg >= 50{
        exam[i].grade = "가"
    } else{
        exam[i].grade = "미달"
    }
}

//출력부
for st in exam{
    print(st)
}

주목해야할 점은 등급을 계산하는 부부이다. 조건문을 이용하여 계산하는 것이 대부분이다. 하지만 미세한 차이지만 더 빠르고  훨씬 짧은 코드로 계산이 가능하다.

typealias stud = (name:String, score:[Int], sum:Int, avg:Int, grade:String)

var exam:[stud] = [
    ("이효리",[77,78,71],0,0,""),
    ("삼효리",[57,58,51],0,0,""),
    ("사효리",[97,98,91],0,0,""),
    ("오효리",[67,68,61],0,0,""),
    ("육효리",[87,88,81],0,0,"")
]

//연산부
for i in 0..<exam.count{
    for k in exam[i].score{
        exam[i].sum += k        //i번째 튜플의 합 계산
    }
    exam[i].avg = exam[i].sum / exam[i].score.count //i번째 튜플의 평균 계산.

    let grade = ["미달","미달","미달","미달","미달","가","양","미","우","수"]
    exam[i].grade = grade[exam[i].avg / 10]
}

//출력부
for st in exam{
    print(st)
}

평균을 10으로 나눈 결과로 인덱스를 정하고, 배열에 저장해둔 등급 결과는 정해둔 인덱스를 이용하여 출력하면 된다. 

코드는 더 짧아지고 컴퓨터는 기존의 조건문의 연산보다 더 적은 연산을 통해 결과를 출력할 수 있다.

'iOS > TJ' 카테고리의 다른 글

day10_func()  (0) 2021.06.17
day09_dictionary, set  (0) 2021.06.16
day07_array  (0) 2021.06.02
day06_control flow_loop  (0) 2021.05.27
day05_control flow_loop  (0) 2021.05.25

Array

배열(Array)이란, 같은 자료형들의 묶음 이라고 생각하면 된다. 같은 자료형의 여러 데이터를 순서대로 저장하는 변수이다.

var 배열이름 : [자료형] = [값1, 값2, 값3, 값4, ... ]

var 배열이름 : Array<자료형> = [값1값2값3값4, ... ]

var arr : [Int] = [20,10,40,70,10]	//배열변수 선언 및 초기화. 배열의 원소는 중복되어도 된다.

print("arr: \(arr)")    		//배열 호출
print("arr[0]: \(arr[0])")    		//배열원소 호출 ::> 배열변수[인덱스번호]
print("arr[1]: \(arr[1])")    		//배열원소 호출 ::> 배열변수[인덱스번호]
print("arr[2]: \(arr[2])")    		//배열원소 호출 ::> 배열변수[인덱스번호]
print("arr[3]: \(arr[3])")    		//배열원소 호출 ::> 배열변수[인덱스번호]
//print("arr[5]: \(arr[5])")    	//없는 원소는 호출 시 에러가 발생한다.

배열의 원소를 호출할 시 배열이름[인덱스번호] 형식을 통해 접근할 수 있다. 배열의 인덱스 번호는 0번부터 시작한다.


빈 배열 선언 및 초기화

//비어있는 배열 생성
var arr1:[Int]          		//초기화하지 않음. 호출불가
var arr2:[Int] = []     		//비어있는 배열로 선언 및 초기화

var arr3:[Int] = [Int]()      		//비어있는 배열로 선언 및 초기화.
var arr4:[Int] = Array<Int>() 		//비어있는 배열로 선언 및 초기화.

var arr5:Array<Int> = [Int]() 		//비어있는 배열로 선언 및 초기화.
var arr6:Array<Int> = Array<Int>() 	//비어있는 배열로 선언 및 초기화.

print("arr2:",arr2)
print("arr3:",arr3)
print("arr4:",arr4)
print("arr5:",arr5)
print("arr6:",arr6)

비어있는 배열을 넣는 방법은 여러가지가 있다. 좌항이나 우항에 한곳이라도 자료형이 명시되어있다면 한 쪽은 자료형 생략도 가능한 점을 알아두자. 변수와 마찬가지로 초기화하지 않은 배열은 호출이 불가능하다.


for문과의 조합

var arr_A = ["호랑이","표범","고양이","재규어","치타","퓨마"]

for i in arr_A{
    print(i)
}

예시와 같이 배열은 for문에서 사용할 경우 편리하게 사용 가능하다. for문의 임시상수 i는 열거형 데이터의 순번을 돌며 원소들을 하나씩 복사해온다는 장점이 있다.

var arr10 = [12,54,67,34,23,98,57,36]
var sum = 0

for i in arr10 {
    sum += i
    print("\(i): \(sum)")
}
print(sum)

배열 속 원소들의 합계를 간단하게 구할 수 있다.


요소추가

배열에 요소를 추가할 수 있는 여러가지 함수가 있다.

append(추가요소)

   ->   배열의 마지막에 요소를 추가한다.

insert(추가요소, at:인덱스)

   ->   인덱스 위치에 요소를 추가한다. 해당 인덱스에 있던 요소는 한자리 밀려난다.

insert(contentsOf:[ 추가요소1, 추가요소2, 추가요소3, ... ], at:인덱스)  

   ->   배열형태의 여러 요소를 해당 인덱스에 한번에 추가한다. 해당 인덱스에 있던 기존 요소는 다음으로 밀려난다.

var arr = [10,20,30]
print("arr: \(arr)")

arr.append(400)		//배열의 마지막 원소 뒤에 요소 400 추가
print("arr.append(400)")
print("arr: \(arr)")

arr.insert(35, at: 1)	//배열의 1번 인덱스 위치에 요소 35 추가
print("arr.insert(35, at: 1)")
print("arr: \(arr)")

arr.insert(contentsOf: [56,33,100], at: 1) //배열의 1번 인덱스에 여러 요소 한번에 추가
print("arr.insert(contentsOf: [56,33,100], at: 1)")
print("arr: \(arr)")

요소삭제

배열 속 요소를 삭제하는 여러가지 함수가 있다.

remove(at:인덱스)

   -> 해당 인덱스의 요소를 삭제한다. 삭제할 요소를 리턴해준다.

removeFirst()

   -> 배열의 가장 첫번째 요소를 삭제한다. 삭제할 요소를 리턴해준다.

removeLast()

   -> 배열의 가장 마지막 요소를 삭제한다. 삭제할 요소를 리턴해준다.

popLast()

   -> 배열의 가장 마지막 요소를 삭제한다. 삭제할 요소를 리턴해준다.(옵셔널)

removeSubrange(범위)

   -> 범위에 해당하는 인덱스의 요소를 삭제한다. 리턴요소가 없다.

dropFirst()

   -> 첫번째 요소를 제외하고 모든 요소을 리턴한다. 본배열에는 변경이 일어나지 않는다.

dropLast()

   -> 마지막 요소를 제외하고 모든 요소를 리턴한다. 본배열에는 변경이 일어나지 않는다.

var arr = [10,20,30,40,50,60,70]
print("arr: \(arr)")

var no = arr.remove(at: 2)          //2번 인덱스 요소 삭제, 삭제할 요소를 반환해준다.
print("arr.remove(): \(arr)")       //본배열에 2번인덱스요소가 삭제된다.
print("arr.remove() return: \(no)") //삭제된 요소는 반환되어 no변수에 복사된다.

no = arr.removeFirst()              //가장 첫번째 요소 삭제, 삭제할 요소를 반환해준다.
print("arr.removerFirst(): \(arr)")
print("arr.removerFirst() return: \(no)")

no = arr.removeLast()               //가장 마지막 요소 삭제, 삭제할 요소를 반환해준다.
print("arr.removerLast(): \(arr)")
print("arr.removerLast() return: \(no)")

no = arr.popLast()!     	//마지막 요소 삭제, 삭제요소 반환. popFirst()는 없다.
print("arr.popLast(): \(arr)")
print("arr.popLast() return: \(no)")

arr = [10,20,30,40,50,60,70]
print("arr:\(arr)")

var sub = arr.removeSubrange(2...4)    //범위삭제. 리턴요소가 없다. 2번, 3번, 4번 인덱스의 요소 삭제.
print("arr.removeSubrange(2...4): \(arr)")
print("arr.removeSubrange(2...4) return: \(sub)")

var arr2 = arr.dropFirst()  	//본 배열에는 변경없음. 첫번째 요소를 제외하고 리턴을 준다.
print("arr: \(arr)")
print("arr.dropFirst() : \(arr2)")

arr2 = arr.dropLast()  		//본 배열에는 변경없음. 마지막 요소를 제외하고 리턴을 준다.
print("arr: \(arr)")
print("arr.dropLast() : \(arr2)")


인덱스

index(of:요소)

   -> 요소가 위치한 인덱스를 반환한다. 여러개가 있을경우, 그중 가장 첫번째 요소의 인덱스를 반환 (옵셔널)

firstIndex(of:요소).    *index(of: )와 같은 함수. 더 이상 index(of:)는 사용하지 않는다.

   -> 요소가 위치한 인덱스를 반환한다. 여러개가 있을경우, 그중 가장 첫번째 요소의 인덱스를 반환 (옵셔널)

lastIndex(of:요소)

   -> 요소가 위치한 인덱스를 반환한다. 여러개가 있을경우, 그중 가장 마지막 요소의 인덱스를 반환 (옵셔널)

arr = [11,44,22,33,44,66,88,33,44,88,66,44,22]
print("arr: \(arr)")

print("arr.index(of: 44)!:",arr.index(of: 44)!)   //해당 요소가 어느 인덱스에 위치하는지 찾아준다. (옵셔널)
print("arr.index(of: 88)!:",arr.index(of: 88)!)   //해당 요소가 어느 인덱스에 위치하는지 찾아준다.
print("arr.index(of: 100):",arr.index(of: 100))   //해당 요소가 어느 인덱스에 위치하는지 찾아준다.
						  //옵셔널이기에 요소가 없다면 nil이 반환된다.
print("arr.firstIndex(of: 44)!:",arr.firstIndex(of: 44)!) //해당 요소 중 첫번째 인덱스를 찾아준다. (옵셔널)
print("arr.lastIndex(of: 44)!:",arr.lastIndex(of: 44)!)  //해당 요소 중 마지막 인덱스를 찾아준다. (옵셔널)

그렇다면 배열의 중간에 위치한 44의 인덱스를 알고싶은 경우는 어떻게 해야할까? 

arr = [11,44,22,33,44,66,88,33,44,88,66,44,22]

arr2 = arr[2...8]
print(arr2)
print(arr2.index(of:44)!) //-> 배열을 옮겨 담아도 기존에 있었던 인덱스 번호를 그대로 가지고 온다.

//그렇다면 나는 중간에 위치한 44의 인덱스를 찾고싶다.
print("arr의 중간 인덱스:",arr[(arr.firstIndex(of: 44)!+1) ..< (arr.lastIndex(of: 44)!)].firstIndex(of: 44)!)

배열의 범위를 제한하여 찾을 수 있다. 즉 첫 번째 44의 인덱스 이후 ~ 마지막 44의 인덱스 이전 이라는 범위에서 44의 첫 번째 인덱스를 검색하면 중간에 위치한 인덱스를 찾게된다. 


요소

contains(요소)

   -> 요소가 존재하는지 bool형태를 반환하여 알려준다.

replaceSubrange(범위, with=[ 요소1, 요소2, 요소3, ... ])

   -> 범위에 해당하는 인덱스의 명시한 요소들로 바꾼다.

count

 

   -> 요소의 갯수를 반환해준다.

arr = [11,22,33,44,55,66,77,88,99]
print("arr:",arr)

print(arr.contains(44))   //요소 중 44가 존재하는지 알려준다.
print(arr.contains(100))   //요소 중 100이 존재하는지 알려준다.

arr.replaceSubrange(2..<4, with: [100,200,300])     //2,3번째 요소를 [100,200,300]으로 교체
print("after \"arr.replaceSubrange(2..<4, with: [100,200,300]\" : \(arr)")

print("arr:",arr)
print("arr.count:",arr.count) //원소갯수 출력


정렬

reverse()

   -> 현재 배열을 역순으로 정렬한다. 리턴값은 없다. 

reversed()

   -> 현재 배열의 역순을 리턴한다. 본 배열은 바뀌지 않는다.

var arr = [11,33,55,44,22,66,77,88,99]		//기존 배열 생성

print("arr:\(arr)\n")				//기존 배열 출력

var arr2 = arr.reversed()			//반전된 값을 arr2로 리턴
print(arr2)					//arr2 출력

print("arr.reversed():",terminator: "")		//줄바꿈 없이 출력
for i in arr2{
    print(i,"",separator: " ",terminator: "")	//줄바꿈 없이 출력
}
print("\n")

print("arr:\(arr)")				//기존배열 출력. 변화가 없다는 것을 확인.

arr.reverse()					//기존 배열을 반전.
print("arr.reverse():\(arr)")			//기존 배열 출력


sort()

   -> 현재 배열을 오름차순으로 정렬한다. 리턴값은 없다. (by:<)가 기본값으로 생략되어있다.

sort(by:<)

   -> 현재 배열을 오름차순으로 정렬한다. 리턴값은 없다.

sort(by:>)

   -> 현재 배열을 내림차순으로 정렬한다. 리턴값은 없다.

var arr = [11,33,55,44,22,66,77,88,99]
print(arr)	//기존배열 출력

arr.sort(by:<)	//기존 배열 오름차순 정렬
print(arr)

arr.sort(by:>)	//기존 배열 내림차순 정렬
print(arr)

arr.sort()	//기존 배열 오름파순 정렬
print(arr)

var txtArr = ["현빈","김우빈","원빈","투빈","장희빈","미스터빈"]
print(txtArr)

txtArr.sort() //문자열 배열도 정렬이 가능하다.
print("text.sort():",txtArr)


sorted()

   -> 현재 배열을 오름차순으로 정렬하여 리턴한다. 본 배열에는 변화가 없다. (by:<)가 기본값으로 생략되어있다.

sorted(by:<)

   -> 현재 배열을 오름차순으로 정렬하여 리턴한다. 본 배열에는 변화가 없다.

sorted(by:>)

   -> 현재 배열을 내림차순으로 정렬하여 리턴한다. 본 배열에는 변화가 없다.

print(arr)		//기존 배열 출력

print(arr.sorted(by:<))	//sorted(by:<)함수 리턴 출력

print(arr.sorted(by:>))	//sorted(by:>)함수 리턴 출력

print(arr.sorted())	////sorted()함수 리턴 출력

print(arr)		//기존배열 출력. 변화가 없다는 것을 확인할 수 있다.


대입

배열은 범위를 사용하여 대입연산이 가능하다.

arr = [11,22,33,44,55,66,77,88,99]
print("arr: \(arr)")

var arr2 = arr[2...4]   //arr의 2번부터 4번요소를 arr2에 대입
print("arr[2...4]: \(arr2)")

arr[5...7] = []     	//빈 배열을 대입하여 범위삭제도 가능하다.
print("after \"arr[5...7] =[]\" : \(arr)")


배열을 이용한 자료관리

배열은 같은 자료형만 묶을 수 있다. 따라서 복합적인 자료를 다룰 때에는 자료형에 맞는 배열을 여러개 생성하여 다룬다.

var name = ["한가인","두가인","삼가인","사가인","오가인"]	//이름이 담기는 배열 생성
var score = [78,56,91,85,64]				//점수를 담는 배열을 생성
var glass = [true, false, false, true, true]		//안경을 썼는지 여부를 담는 배열 생성

print("0: \(name[0]) \(score[0]) \(glass[0])")		//각 배열의 요소를 가져오는 방법.
print("1: \(name[1]) \(score[1]) \(glass[1])")
print("2: \(name[2]) \(score[2]) \(glass[2])")
print("3: \(name[3]) \(score[3]) \(glass[3])")
print("4: \(name[4]) \(score[4]) \(glass[4])")
print("--------------------------------------\n")

for i in 0 ..< name.count{    				//count를 사용하여 범위를 정한다.
    print("\(i): \(name[i]) \(score[i]) \(glass[i])")	//반복문을 사용하여 요소를 가져올 수 있다.
}

예시와 같이 반복문과 배열을 사용하면 복합적인 자료를 손쉽에 처리할 수 있다.

'iOS > TJ' 카테고리의 다른 글

day09_dictionary, set  (0) 2021.06.16
day08_multiArray, tuple  (0) 2021.06.03
day06_control flow_loop  (0) 2021.05.27
day05_control flow_loop  (0) 2021.05.25
day04_control flow_conditional  (0) 2021.05.22

+ Recent posts