ios개발/함수형 프로그래밍

<함수형 프로그래밍> 1편,Swift 3 functional programming 책 정리 내용

studying develop 2020. 4. 15. 02:14

책을 선택 했다. Swift 3 functional programming

 

일단 챕터 1 . Getting Started with Functional Programming in Swift

 

- why functional programming matters?

- what is functional programming?

- swift language basics

- immutability

- first-class , higher-order, and pure functions

- Optionals and pattern matching

- closures

- type aliasing

 


why functional programming matters?

일단 첫페이지 보니까. 소프트웨어가 복잡해지고 그러니까 계층을 분리하는등 개발자가 개발 책임을 나누기 쉽게 바뀌는게 추세인데, 이제 지금까지는 객체지향이 절차형 보다 업그레이드된 버전이였던거 같다. 객체 지향은 소프트웨어를 패키지들,크래스들,인터페이스들 그리고 메소드들등으로 분리한다. 클래스는 인스턴스와 오브젝트들을 만들기 위한 요리법 같은거라함 ㅋㅋ

 

근데 이제 객체 지향에서 building blocks??를 서로 연결하는것은 그것들을 분리하여 나누는것 만큼이나 어렵다함. 서로 다른 객체들 사이의 연결은 strong coupling을 그들 사이에 발생시킬 수 있다함. coupling은 oop에서 가장 복잡한 녀석이라함. 모듈이나 클래스의 변화가 coupled된 모듈들과 클래스들의 변화를 강요할 수 있음. 또한 , 특정 모듈이나 클래스는 coupled되서 재사용하기 아주 어려울수 있다함.

(coupled : 결합)

 

개발자들은 소프트웨어를 잘 설계하고 서로 다른 원리들과 디자인 패턴들을 적용함으로서 결합을 루스하게 하려한다. 예를 들면 single-responsibility, open-closed, liskov substitution, interface segregation and dependency inversion(solid) 원리들을 잘 적용하면 소프트웨어를 유지하고 확장하기 쉽게 도와준다. 

 

일단 위의 원리들을 잘 모른다. 근데 어디서 들어본거 같음.;; 객체지향보다 나온건가.

 

그럼에도 불구하고 결합을 감소시키고 소프트웨어 구조를 단순화하고, 메모리를 관리하고, 객체들을 참조하고 서로 다른 객체들을 시험하는 것은 여전히 어렵다. oop에서는 , 객체들을 열고 변이가 가능하기 때문이다.

 

퓨어 함수는 함수형 프로그래밍에서 가장 중요한 녀석이다. 퓨어 함수들은 자신들 외부의 데이터에 의존하지 않고 외부 데이터를 변화 시키지도 않는다. 퓨어 함수들은 테스트 하기 쉽다 왜냐하면 동일한 결과를 항상 줄것이니까.

 

서로 다른 스레드들이나 코어에서 퓨어 함수들은 멀티 스레딩이나 프로세싱을 다룰 필요 없이 사용할 수 있다. 이건 객체지향에 비해 아주 중요한 효과라함. 요즘 컴터는 프로세서가 몇개 있는지 모를정도로 많은데 이런 프로그래밍 특성이 매우 좋다함.

 

어쨋든 퓨어 함수를 이용하면 복잡한 멀티 코어 프로그래밍 기법을 사용하지 않아도 됨, 그리고 퓨어 함수는 외부 데이터에 의존하지도 않고, 변이시키지도 않으니까 다른 부분을 건드리지 않고 수정하기 매우 편리하다.

 


what is functional programming?

it is a style of programming that models computations as the evaluation of expressions. 함수형 프로그래밍은 표현식을 계산함으로서 연산들을 모델링하는 스타일이다. 함수형 프로그래밍은 (선언형)declarative programming 스타일이다, oop는 (명령적인)imperative programming인데 반해.

 

이론적으로, 함수형 프로그래밍은 category theory를 사용한다, 수학의 한 분야다. 수학 공부가 필수는 아니지만, 공부하면 발전된 개념인 functors, applicative functors, and monads를 이해하는데 도움될 것이다. 우리는 category theory, 그리고 그것과 함수형 프로그래밍의 관계도 알아 볼 것이다.

 

함수형 프로그래밍에서 함수는 기초적인 빌딩 블록들이다. 객체지향에서는, 프로그램들을 클래스들과 선언문들로 구성한다, 하지만 이런 것들은 실행되면 클래스들의 상태를 변화시킬 수 있다.

 

함수형 프로그래밍은 변이할 수 있는 상태들을 피한다. 변이 상태를 피하는 건 훨씬 테스트,읽기 코드 이해가 쉽다. 하지만, 파일이나 디비를 다룰때는 못 피할수도 있다.

 

함수형 프로그래밍은 함수들이 first-class이도록 요구한다. first-class 함수들은 다른 값들 처럼 다루고 다른 함수들로 넘기거나 함수의 반환 값으로 리턴될 수 있다.

 

함수들을 다른 함수들의 인자들로서 higher-order 함수들로 형성할수 있다. higer-order 함수들을 사용해 코드 리팩터를 하고 반복되는 코드를 감소시킨다. higher-order 함수들을 사용해 domain-specific languages를 구현하기 위해 사용할 수 있다.

 

함수들은 퓨어해서 외부 데이터를 변이 시키지 않는다. 퓨어 함수들은 실행시 항상 동일 값을 반환한다. 퓨어 함수의 이런 성질을 referential transparency라 부른다 그리고 이 특징은 코드상에서 equational reasoning을 수행할 수 있도록 해준다.

 

예시 코드가 있는데, lazy 키워드로 리스트의 첫 원소만 map 연산되도록 한다.


The Swift programming language

스위프트는 OOP와 함수형 프로그래밍 패러다임을 갖는 프로토콜 지향을 결합한 오픈소스 하이브리드 언어이다. 스위프트는 objc와 사용 가능하다. 우분투 리눅스에서도 가능하다.

 

Swift features

스위프트는 많은 개념들을 다른 언어들에서 빌려왔다, 스칼라, 하스캘,c#,러스트 그리고 obj-c는 아래의 특징들을 갖는다.

 

Modern syntax

스위프트는 obj-c처럼 언어의 verbosity를 제거하는 현대 문법을 갖는다. *Verbosity or verboseness is speech or writing that uses more words than necessary, e.g. "in spite of the fact that" rather than "although".

 

둘의 비교 예시 코드가 있는데, obj-c를 못읽어서 ;;

 

Type safety and type inference

스위프트는 타입 세이프 언어이다. 스위프트는 타입 세이프 콜렉션들을 제공한다. 스위프트는 자동적으로 타입 추론 메커니즘에 의해 자동적으로 타입을 유도해낸다. 예를 들면 주어진 예시는 string이라 표기 안해도 컴파일 타임때 스트링으로 파악한다.

 

Immutability

스위프트는 불변 값을 아주 쉽게 정의한다 - 즉 불변값은 상수이다 - 그리고 함수형 프로그래밍에서 불변값은 아주 중요한 핵심 개념으로서 함수형 프로그래밍에 힘을 준다. 상수는 초기화 하면, 대체가 안된다. 자바에서도 불변성은 줄수 있지만 스위프트 처럼 쉽지는 않다. 불변 타입을 스위프트에서 주려면,  커스텀 타입이든,콜렉션 타입이든, 구조체던 let 키워드를 사용하면 된다. 

 

Stateless programming

스위프트는 값으로 전달(pass by value)될 수 있고 stateless할 수 있는 아주 강력한 structures 와 enumeration들을 제공한다. 그러므로, 그들은 아주 효율적이다. stateless 프로그래밍은 병렬성과 멀티스레딩을 아주 간단하게 한다.

 

스테이트 리스 프로그래밍이 뭐지;; 구조체와 이넘, 아 , 값으로 전달된다고

 

First-class functions

함수들은 스위프트에서 루비,js,go에서 처럼 저장될수 있고,전달될 수 있고, 반환될 수 있는 first-class types이다. first-class 함수들은 스위프트의 함수형 프로그래밍에 힘을 준다.

 

Higher-order functions

higher-order 함수들은 다른 함수들을 자기의 파라미터들로 받을 수 있다. 스위프트는 map,filte 그리고 reduce같은 higher-order 함수들을 제공한다. 또한 스위프트에서, 우린 우리만의 higher-order 함수들과 DSLs을 개발할 수 있다.

 

 

pg30-31

 

Pattern matching

패턴 매칭은 destructure values 하고 서로 다른 스위치 케이스들을 올바른 값 매칭에 근거해 매칭시키는 것이다. (???)

패턴 매칭 능력들은 스칼라,하스칼등에서도 존재한다. 스위프트는 막강한 switch case들과 if-case들을 where 절과 함께 제공한다.

 

음 이게 머지??

Generics

스위프트는 특정 타입에 한정되지 않은 코드를 작성할 수 있고 서로 다른 타입들에 대해 활용할 수 있습니다.

 

Closures

클로저들은 전달될 수 있는 코드 블록들입니다. 클로저들은 그 안에 정의된 상수들과 변수들의 컨텍스를 캡쳐한다. 스위프트는 obj-c보다 단순한 코드 블록들을 제공합니다.

 

Subscripts

스위프트는 콜렉션들, 리스드들, 시퀀스들, 또는 커스텀 타입의 맴버들에 지름길인 subsript들을 제공한다. subsript들은 값을 setting,getting 메소드들을 분리할 필요없이 set,get 하기 위해 사용할 수 있다.

 

Optional chaining

스위프트는 몇몇 또는 none 값을 갖는 옵셔널 타입들을 갖는다. 스위프트는 옵셔널을 안전하고 효율적으로 사용할 수 있게 옵셔널 체이닝을 제공한다. 옵셔널 체이닝은 우리가 닐일수 있는 옵셔널 타입들 프로퍼티들, 메소드들, 그리고 subsript들을 호출하고 쿼리할 수 있도록 힘을준다.

 

음 그래서 옵셔널 체이닝을 통해 음 다양한 방법으로 사용하기 쉽다는거 같음.

Extensions

스위프트는 obj-c와 유사한 extension들을 제공한다. extension들은 이미 존재하는 클래스,구조체,이넘, 또는 프로토콜 타입에 새로운 기능을 더할수 있습니다, 비록 이게 닫힌 소스라 할지라도.

 

extension은 닫힌 소스에 대해서 확장할 수 있다는게 매력인가 보다.

 

obj-c and swift bridging headers

브리징 헤더들은 우리 프로젝트에 오비제이씨랑 스위프트를 섞어 우리가 힘을 얻게해준다. 이 기능은 우리가 이전에 작성한 오비제이씨 코드를 스위프트 프로젝트들 그리고 다른데서 사용할 수 있게 해준다.

 

[https://play.google.com/books/reader?id=LP9vDQAAQBAJ&hl=ko&pg=GBS.PA13.w.13.0.10] pg30-31

 

Automatic Reference Counting

스위프트는 메모리 관리를 ARC (automatic reference counting)으로 관리한다, obj-c,자바,c#은 가비지 콜렉션을 사용한다. ARC는 자원들을 initialize, deinitialize하기 위해 사용한다, 그럼으로서 클래스 객체가 필요 없으면 메모리 할당들을 해제한다. ARC는 메모리 자원을 효율적으로 사용하기 위해 코드 인스턴스들 안에서 tracks retains and releases한다. 

 

REPL and Playground

xcode는 read eval print loop ( REPL) 커맨드 라인 환경을 통해 프로그램을 작성하지 않고 스위프트 프로그래밍 언어를 실험할 수 있게 해준다. (뭔 소리이지??) . 또한 스위프트는 스위프트 코드 snippet들을 빠르게 테스트 하고 비주얼적으로 결과를 실시간으로 볼수 있는 있는 플레이그라운드를 제공한다. 플레이그라운드을 이 책에서 아주 많이 사용할것이다.

 


Language basics

이 챕터에서는 스위프트 언어에 대해 간략히 소개하고, 섭 챕터는 이후의 챕터들에서 상세히 소개할 것입니다.

 

Type safety and type inference

스위프트는 type safe한 언어이다. 그 의미는 우리가 상수,변수, 또는 expressionn(이게 뭐지??)의 타입을 한번 결정하면 변경할 수 없다. 또한, type safe한 스위프트의 성격은 우리가 타입 미스매칭을 컴파일 타임때 찾을 수 있도록 도와준다.

 

스위프트는 type inference를 제공한다. 타입 추론. 스위프트는 변수,상수,표현식의 타입을 자동적으로 추론한다 그리고 우리는 그 타입들을 정의할때 결정하지 않아도 된다. 

 

Type annotation

annotation이 주석이라는데, 음 ㅋㅋ?

스위프트에서, 타입을 주석처리할 수 있다? 다른말로하면 , 명시적으로 변수나, 표현식의 타입을 결정할수 있다. 음 예시 보니까 튜플같은걸 expression이라 하는건가?

 

Type aliases

type aliases는 존재하는 타입에 대안할 수 있는 이름을 제공한다. 우리는 aliases를 typealias 키워드로 정의한다. type aliases는 우리가 존재하는 타입을 문맥상 더 적절한 이름으로 언급하려 할때 아주 유리하다, 예를들면 특정 크기의 외부 출처의 데이터를 작업하려 할떄 같이??

예를 들면 다음 예시에서, 우리는 alias를 이후 코드에서 사용될 수 있는 unsigned 32-bit integer에 제공한다.

 

typealias UnsignedInteger = UInt32

typealias 정의들을 클로져와 함수 정으를 간단히 하는데 사용할 수 있다. (???)

 

Immutability

스위프트는 변수를 변이적,불변이적으로 정의할 수 있다. let이 불변적이고, var이 변이적이다. 

 

함수형 프로그래밍에서, 프로퍼티들을 상수 또는 불변이적으로 최대한 정의하는 것이 권장된다. 불변이적 변수들은 추적하기 편하고 에러를잘 발생 안시킨다. 하지만 CoreData 프로그래밍, SDK는 변이적인 프로퍼티들을 요구한다. 이런 경우에는 변이적 변수를 사용하는게 권장된다 (...??왜)

 

Tuples

스위프트는 복수의 값들을 단일 결합 값에 넣을수 있게 튜플을 제공한다. 튜플은 multireturn 함수들을 위한 리턴 타입으로 사용될 수 있다.

 

Optionals

스위프트는 값이 존재하지 않을수도 있는 옵셔널을 제공한다. 옵셔널은 값이 있을수도 없을수도 있다. ? 심볼을 옵셔널 변수를 정의하기 위해 사용한다. 

! 심볼은 강제적으로 옵셔널의 값을 벗기기위해 사용한다. force-unwrapping은 에러를 발생시킬수도 있다 그러므로 값이 있는지 확신하지 못하면 절대 사용하지 말아라....

더 좋은 접근법은 옵셔널 바인딩 기술을 사용해 옵셔널이 값을 갖는지 확인해라. if사용하라는 말.

 

Basic operators

스위프트는 다음 기본 연산자들을 제공한다.

=

+,-,*,/,%

-i,+i

+=,-=,*=

a == b, a != b, a>b, a<b, a<=b

q ? a1:a2

 

아 이건 볼때마다 낯선대

Nil coalescing a ?? b 는 옵셔널 a를 벗긴다 만약 값이 있으면, a가 nil이면 default value b를 반환한다.

 

range operators

a...b , a..<b

 

logical operators

!a , a && b , a || b

 

Strings and characters

음 이 부분을 잘 몰라서 많이 해맨거 같다.

 

스위프트에서, 스트링은 정렬된 문자열들의 콜렉션이다. String은 구조체이지 클래스가 아니다. 구조체들은 스위프트에서 value type들이다. 그러므로, 모든 String은 value type이고 value들이 넘어간다, 레퍼런스로 넘기는게 아니다.

 

Immutability

String들은 let으로 불변성을 위해 정의할수 있다. var로 정의된 String은 변이적이다. 

?? 불변적으로 정의하지 못할수도 있는건가??

 

String literals

string literal들은 스트링 인스턴스를 만들기 위해 사용할수 있다. 다음 코드는 스트링 리터럴로 초기화한다.

let aVegetable = "Arugula"

 

Empty Strings

빈 스트링은 다음처럼 초기화할수 있다.

var anEmptyString = ""
var anotherEmptyString = String()

이 두개의 스트링들은 모두 비어 있고 서로 동등하다. 스트링이 빈지는 isEmpty로 확인 가능하다.

if anEmptyString.isEmpty{
	print("Strnig is empty")
}

 

Concatenating strings and characters

문자열들과 문자들은 concatenate 할수 있다.

 

let string1 = "Hello"
let string2 = "Mr"
var welcome = string1 + string2

var instruction = "Follow us please"
instruction += string2

let exclamationMark : Character = "!"
welcome.append(exclamationMark)

 

String intrepolation

String interpolation은 상수들,변수들,리터럴 그리고 표현식들의 값을 스트링 리터럴 안에 섞어서 새로운 스트링값을 만든다.

 

let multiplier = 3
let message = "\(multplier) times 7.5 is \(Double(multiplier)*7.5)"

//message is "3 times 7.5 is 22.5"

 

String comparison

문자열들은 ==를 대등함 비교에 사용할수 있고, !=를 대등치 않음으로 사용할수 있다.

 

hasPrefix 그리고 hasSuffix 메소드들을 prefix 그리고 suffix 동일성 확인에 사용할수 있다.

 

let str = "hello, c world!"

str.hasPrefix("hello") // true
str.hasPrefix("world") // false

str.hasSuffix("hello") // false
str.hasSuffix("world") //true

이렇게 사용할수 있다.

 

Collections

스위프트는 배열들,딕셔너리들, 집합들 같은 타입을 갖는 콜렉션들을 제공한다. 스위프트에서는 obj-c랑 다르게 모든 콜렉션의 요소들은 동일한 타입을 갖을것이고 우리는 콜렉션의 타입을 정의한 후로 변경할 수 없다.

 

위 말이 무슨말인지 잘 모르겠다. 콜렉션들에 들어간 타입들이 모두 동일한 타입들이라는거 같고, 한번 타입 정의하면 타입이 변경될수는 없다는건가.

 

우리는 콜렉션들을 let과 var로 정의할수 있다, 다음 예시에서 처럼.

//Arrays and Dictionaries 
var cheese = ["Brie","Tete de moine","Cambozola","Camembert"]
cheese[2] = "Roquefort"
var cheeseWinePairs = ["Brie" :"Chardonnay", "Camembert":"Champagne","Gruyere":"Sauvignon Blanc"]

cheeseWinePairs["Cheddar"]="Cabarnet Sauvigon"
//To create an empty array or dictionary let emptyArray = [String]()
let emptyDictionary = Dictionary<String,Float>()
cheese = []
cheeseWinePairs = [:]

음 위의 코드를 봐도 뭘 전달하려는 건지 잘 모르겠다. 변이성이랑 불변성 보여주는건가.

 

for-in 루프는 콜렉션들 내부의 모든 아이템들을 순회하는데 사용할수 있다.

 

Control flows

for loops

for과 for-in 루프를 제공한다.

for x in list

for var i=0;i<3;i++

for i in 0...3

 

while loops

while n < 100 {

}


repeat {

} while m < 100

 

stride

let fourToTwo = Array(stride (from : 4, to : 1, by: -1))

let fourToOne = Array(stride (from:4,through: 1, by: -1))

 

if

 

switch

다른 C 기반의 언어들과 다르게, 스위프트는 break 선언문이 필요 없다. 스위치 선언문을 범위 매칭을 위해 사용할수 있고, where 절들로 추가적인 조건문들을 확인하기 위해 사용할수 있다. 

switch aNumber{

	case "one" : let one = "One"
    case "Two" , "Three" : let twoOrThree = "TwoOrThree"
    case let x where x.hasSuffix("Five") : let fourOrFive = "its \(x)"
    default : let anyOtherNumber = "Any other number"

}
    

guard

guard 선언문은 빠른 종료를 위해 사용할수 있습니다. 이후 코드를 진행하려면 guard 선언문은 트루여야 합니다.

 

functions

함수들은 특정 작업을 수행하는 self-contained 코드의 블록들이다.

 

스위프트에서, 함수는 first-class citizen이다, 저장되고, 전달되고 반환될수 있다. 함수들은 다른 함수들을 인자로 받는 higer-order 함수들로 curry 되고 정의될수 있다. 함수들은 복수의 인풋 파라미터들과 리턴들을 튜플로 받거나 리턴할수 있다. 

 

저장되는건 어떤 의미지?

 

함수는 variadic parameter들을 가질수있다. 

func sumOf(numbers: Int...) -> (Int,Int){
	
    
    
    
	return (sum,counter)
}

sumOf()
sumOf(numbers:4,6,23)
    

mutable parameter도 가능했는데, 3.0 이후로 삭제됨.

 

inout 파라미터들을 가질수 있다. 

func swapTwoInts(a: inout Int, b: inout Int){
	let tmp = a
    a = b
    b = tmp
}

inout 파라미터들은 함수적 스위프트에서 딱히 선호되지 않는다 그들이 상태들을 변이시키고 함수를 불순하게 만들기 때문에.

 

스위프트에서는 네스티드 함수들을 정의할수 있다. 

 

스위프트에서는 다른 함수들을 리턴할수 있다. 

func makeIncreamenter() -> (Int -> Int) {
	func addOne(number: Int) -> Int{
    	return 1 + number
    }
	return addOne
}

var increment = makeIncrementer()
invrement(7)

Closures

특정 기능들을 제공하고 저장되고, 전달되고, 사용할수 있는 self-contained 코드 블록이다. 클로저들은 C와 오비제이씨에서의 블록들과 동일하다. 클로저들은 상수들과 변수들이 정의되는 컨텍스트 상에서 레퍼런스들을 캡처하고 저장할수 있다. 네스티드 함수들은 클로저들의 특별한 경우이다. 클로저들은 변수,상수,그리고 type aliases에 저장될수 있는 참조 타입들이다. 그들은 함수에 전달되고 리턴될수 있다.

 

스위프트에서 다양한 선언들임.

//as a variable:
var closureName: (parameterTypes) -> (returnType)

//as an optional variable:
var closureName: ((parameterTypes) -> (returnType))?

//As a type alias:
typealias closureType = (parameterTypes) -> (returnType)

 

Map,filter, and reduce

스위프트의 함수들은 higher-order 함수들이다.

 

Map

map 함수는 배열의 요소들을 변형함으로서 문제를 해결한다.

코드에 주석은 무슨 말이지;;

//Return an "array" containing the results of calling 'transform(x)' on each element 'x' of 'self'
//func map<U>(transform: (T) -> U) ->[U]

let numbers = [10,30,91,50,100,39,12]
var formattedNumbers : [String] = []

for number in numbers {
	let formattedNumber = "\(number)$"
    
    formattedNumbers.append(formattedNumber)
}

let mappedNumbers = numbers.map { "\($0)$" }

 

Filter

filter 함수는 bool을 리턴하는 함수를 갖는다, 함수는 배열안에 요소들이 들어있다, bool은 element이 결과 배열에 포함되어야 하는지 여부를 결정한다.

 

//return an array containing the elements x of self for which includeElement(x) is true
//func filter(includeElement: (T) -> Bool) -> [T]
let someEvenNumbers = numbers.filter { $0 % 2 == 0 }

 

Reduce 

reduce 함수는 배열을 reduce 하여 단일 값으로 변환합니다. reduce는 두가지 파라미터들을 갖는다 : 두가지 값으로 시작 값 그리고 함수, 함수는 running total과 배열의 element들을 파라미터들을 갖고 새로운 러닝 토탈을 리턴합니다.

 

//Return the result of repeatedly calling 'combine' with an accumulated value initialized to 'initial' and each element of 'self', in turn, that is return
//combine(combine(...combine(combine(initial,self[0]),self[1]),...self[count-2]),self[count-1])
//func reduce<U>(initial: U, combine: (U,T) -> U) -> U
let total = numbers.reduce(0) { $0 + $1 }

이후는 종이책으로 일단 보는중 ㅎㅎ..;