무냐의 개발일지

[OSSU] <Programming Language, Part A> / Week4 본문

OSSU_CS coursework

[OSSU] <Programming Language, Part A> / Week4

무냐코드 2024. 5. 28. 00:14

 

함수 자체를 인수로 넣는 거!

First-Class function : 다른 함수의 인수로 아무데나 넣을 수 있는 성질의 함수 : 고차 함수 (higher-order funciton)

Function Closure : First-class function 만들고 나서, 이걸 인수, local variable, 환경에 있는 것들 다 사용할 수 있는 함수

 

 

| Anonymous function

1) 
fun triple x = 3*x

fun triple_n_times1 (n,x) = n_times(triple,n,x)

2) 
fun triple_n_times2 (n,x) =
  let fun triple x = 3*x in n_times(triple,n,x) end

3) 
fun triple_n_times3 (n,x) = 
    n_times((let fun triple y = 3*y in triple end), n, x)

4)
fun triple_n_times4 (n,x) = n_times((fn y => 3*y), n, x)

 

마지막 방식으로 코드를 짜는게 제일 깔끔하다!! 

let in end 식으로 써야 binding이 아니라 expression이 돼서 코드가 작동하는데

이거를 fn y => 3*y 이런 식으로 쓸 수도 있다 .얘는 함수 이름을 지어줄 수 없으니까 그냥 fn 과 변수로 써준다 

 

 

Anonymous functions are expressed in the form 𝑓𝑛 [𝑎𝑟𝑔𝑢𝑚𝑒𝑛𝑡𝑠]=>[𝑓𝑢𝑛𝑐𝑡𝑖𝑜𝑛 𝑏𝑜𝑑𝑦]. The answer 𝑓𝑛 𝑥=>𝑥+3 is of this form where 𝑥 is the [𝑎𝑟𝑔𝑢𝑚𝑒𝑛𝑡𝑠] and 𝑥+3 is the [𝑓𝑢𝑛𝑐𝑡𝑖𝑜𝑛 𝑏𝑜𝑑𝑦].

 

한 곳에만 이 특정 함수가 필요한 경우라면 anonymous function(익명의 이름없는 함수)를 쓰는게 더 간편해!!

 

 

| 쓰잘데기 없는 function wrapping

 (* bad style: the if e then true else false of functions  *)
fun nth_tail (n,xs) = n_times((fn y => tl y), n, xs)

(* good style: *)
fun nth_tail (n,x) = n_times(tl, n, x)

(* bad style *)
fun rev xs = List.rev xs

val rev = fn xs => List.rev xs

(* good style *)
val rev = List.rev

 

tl, rev 자체가 기능이 있으니까, 그대로 쓰는거다

 

open List 

이렇게 치면 List에 내장된 모든 함수 확인 가능 

 

| Map, Filter (매우 중요하고, 자주 쓰임)

Famous High-order function !!!

 

map ; 하나의 함수를 여러개에 다 적용시키는 것

filter ; true인 애들만 남기고, 걸러내는것

 

fun map (f,xs) =
    case xs of
	[] => []
      | x::xs' => (f x)::(map(f,xs'))


val x2 = map (hd, [[1,2],[3,4],[5,6,7]])


fun filter (f,xs) =
    case xs of
	[] => []
      | x::xs' => if f x
		  then x::(filter (f,xs'))
		  else filter (f,xs')

fun all_even xs = 
    filter(is_even,xs)

 

 

 

predicates : bool을 return하는 표현식

 

| Lexical Scope

국지적인 정의

 

(* second example *)
fun f g = 
    let 
        val x = 3
    in
        g 2
    end
val x = 4
fun h y = x + y 
val z = f h

 

여기서 일단 x=3는 본문에 쓰이지 않으니까, 쓸데없는 정의가 된다

fun h y = x + y 

이거는 fun h y = 4 + y 가 된다

마지막 val z = f h

 

val z = f h (h 는 4를 더하는 함수지)

val z = f + 4 

여기서 f g 는 그냥 g 2 를 부르는 함수네,

그러면 f 는 그냥 2를 부를테니까

val z = 2 + 4 = 6이 된다 

 

 

 

| Currying 

 

자꾸만 카레를 떠올리게 하는 이름인데...

합성함수를 표현하는 방식이다. 고딩때 배웠던 딱 그 표기법

(* ('b -> 'c) * ('a -> 'b) -> ('a -> 'c) : function composition (합성함수) *)
fun compose (f,g) = fn x => f (g x)

fun sqrt_of_abs i = Math.sqrt(Real.fromInt (abs i))

fun sqrt_of_abs i = (Math.sqrt o Real.fromInt o abs) i

val sqrt_of_abs = Math.sqrt o Real.fromInt o abs

가장 오른쪽의 abs 부터 오른쪽으로 쭉 빠져나오면서 적용되는 것

 

이걸 왼쪽부터 읽고싶어서 만들어낸 infix

보통 | >  요렇게 생겨먹은걸로 많이 쓴다 

(* tells the parser !> is a function that appears between its two arguments *)
infix !> 

(* operator more commonly written |>, but that confuses the current version
   of SML Mode for Emacs, leading to bad editing and formatting *)

(* definition of the pipeline operator *)
fun x !> f = f x

(*오른쪽에서 왼쪽으로 읽지 않고, 편하게 왼쪽에서부터 따라가게 만들어주는 장치임*)
fun sqrt_of_abs i = i !> abs !> Real.fromInt !> Math.sqrt

fun backup1 (f,g) = fn x => case f x of
                                NONE => g x 
                              | SOME y => y

(* 어떤 에러라도 뜨면 g를 내놔라 *)
fun backup2 (f,g) = fn x => f x handle _ => g x

 

 

| Closure and Recomputation

(*매번 부른다. s의 사이즈를 구하는 걸 비교할때마다 한다. list의 length 횟수만큼 한다*)
fun allShorterThan1 (xs,s) = 
    filter (fn x => String.size x < (print "!"; String.size s), xs)

(*local var i 를 만든다. 답은 1번과 같지만, list가 길어질수록 이게 더 효율적임
s의 사이즈를 구하는 걸 1번만 한다*)
fun allShorterThan2 (xs,s) =
    let 
	val i = (print "!"; String.size s)
    in
	filter(fn x => String.size x < i, xs)
    end

아래의 경우가 local variable이 있는 경우로, 더 효율적임 

let binding을 한번만 돌린거임 ( No Recomputation )

 

 

 

| FOLD

 

 

| Function Composition (합성함수) 

오른쪽에서 왼쪽으로 읽는거다 

 

(* ('b -> 'c) * ('a -> 'b) -> ('a -> 'c) : function composition (합성함수) *)
fun compose (f,g) = fn x => f (g x)

fun sqrt_of_abs i = Math.sqrt(Real.fromInt (abs i))

fun sqrt_of_abs i = (Math.sqrt o Real.fromInt o abs) i

val sqrt_of_abs = Math.sqrt o Real.fromInt o abs

 

Pipeline 

* 오른쪽에서 왼쪽으로 읽지 않고, 편하게 왼쪽에서부터 따라가게 만들어주는 장치임*

 

똑같은 펑션임

fun backup1 (f,g) = fn x => case f x of
                                NONE => g x 
                              | SOME y => y

(* 어떤 에러라도 뜨면 g를 내놔라 *)
fun backup2 (f,g) = fn x => f x handle _ => g x

 

 

| Currying

Currying is a technique in functional programming that allows you to transform a function that takes multiple arguments into a sequence of functions that each take one argument. This can make your code more readable, reusable, and modular, as well as enable partial application and higher-order functions.2023. 10. 18.

 

웬만하면 모든 언어가 다 curried 된 상태임 

 

 

 

 

 

 

| Mutable References

 

z -> x 를 가리키므로 같은 걸 가리킨다

:= 는 값을 바꾸는거다. val _ = x := 43 은 곧 x를 43으로 바꾸겠다는 것

 

마지막에 w는 y는 42그대로고 z는 x값이 바뀌었으므로 z도 그대로 바뀐게 되기 때문에 43 ==> 85

 

 

| CallBacks

ML에서 많이 나오는데, 말 그대로 코드를 돌렸을 때 실행한 시간이나 횟수를 세는 함수 역할을 한다

파이썬에서 tqdm이 시간을 세는건데 이건 콜백은 아니지만 유사해보인다

(* Programming Languages, Dan Grossman *)
(* Section 3: Callbacks *)

(* these two bindings would be internal (private) to the library *)
val cbs : (int -> unit) list ref = ref []
fun onEvent i =
   let fun loop fs =
        case fs of 
          [] => ()
        | f::fs' => (f i; loop fs')
    in loop (!cbs) end

(* clients call only this function (public interface to the library) *)
fun onKeyEvent f = cbs := f::(!cbs)
(*callbacks =assign=  one more element +  (previous) *)

(* some clients where closures are essential
   notice different environments use bindings of different types
 *)
val timesPressed = ref 0
val _ = onKeyEvent (fn _ => timesPressed := (!timesPressed) + 1)

fun printIfPressed i =
    onKeyEvent (fn j => if i=j
                        then print ("you pressed " ^ Int.toString i ^ "\n")
                        else ())

val _ = printIfPressed 4
val _ = printIfPressed 11
val _ = printIfPressed 23
val _ = printIfPressed 4

onEvent 11
!timesPressed
onEvent 13
!timesPressed
onEvent 79
!timesPressed

(*v
al it = () : unit
- 
val it = 1 : int
- 
val it = 1 : int
- 
val it = () : unit
- 
val it = 2 : int
- 
val it = () : unit
- 
val it = 3 : int*)

 

 

 

List.last

val it = fn : 'a list -> 'a


List.map

val it = fn : ('a -> 'b) -> 'a list -> 'b list

 

structure x = List
signature x = LIST

이렇게 하면 리스트의 모든 함수가 나온다