본문 바로가기
학교/CPL

CPL 2-3: Higher Order Functions

by Hongwoo 2024. 3. 27.
반응형

목차

Abstract Syntax Tree (ASTs)

context-free syntax

  Expr.NumExt       = INT      // integer literals
  Expr.TrueExt      = [true]
  Expr.FalseExt     = [false]
  Expr.IdExt        = ID

  Expr.UnOpExt      = [([UnOp] [Expr])]
  Expr.BinOpExt     = [([BinOp] [Expr] [Expr])]

  UnOp.MIN          = [-]
  UnOp.NOT          = [not]
  UnOp.HEAD         = [head]
  UnOp.TAIL         = [tail]
  UnOp.ISNIL        = [is-nil]
  UnOp.ISLIST       = [is-list]

  BinOp.PLUS        = [+]
  BinOp.MULT        = [*]
  BinOp.MINUS       = [-]
  BinOp.AND         = [and]
  BinOp.OR          = [or]
  BinOp.NUMEQ       = [num=]
  BinOp.NUMLT       = [num<]
  BinOp.NUMGT       = [num>]
  BinOp.CONS        = [cons]

  Expr.IfExt        = [(if [Expr] [Expr] [Expr])]
  Expr.NilExt       = [nil]
  Expr.ListExt      = [(list [Expr*])]

  Expr.FdExt        = [(lambda ([ID*]) [Expr])]
  Expr.AppExt       = [([Expr] [Expr*])]
  Expr.LetExt       = [(let ([LetBind+]) [Expr])]
  LetBind.LetBind   = [([ID] [Expr])]

  Expr.RecLamExt    = [(rec-lam [ID] ([ID]) [Expr])]
  Expr.LetRecExt    = [(letrec ([LetBind+]) [Expr])]

 

 

Example 1: Converting Paret to AST

((lambda (x y) (+ x y)) 3 1)

 

 

Example 2:

((lambda (x) (+ x 1)) 2)

 

Example 3:

((lambda (x) ((lambda (x) (+ x 1)) 2)) 3)

 

 

Example 4:

(
    (rec-lam sum (n) 
        (if 
            (num= n 0)
            0
            (+ n (sum (- n 1)))
        )
    ) 
    5
)

 

 

 

Example 5:

(let (
      (x true) 
      (y false)
     ) 
     (and x y)
)

 

 

Example 6:

(letrec
  ((fib
    (lambda (n) *)
  ))
  (fib 5)
)

 

 

 

Translating Functions

Example 1:

public static int increment(int a) {
    return a + 1;
}
(lambda (a)
    (+ a 1)
)

 

 

Example 2:

public static boolean isPositive(int num) {
    if(num > 0) {
        return true;
    } else {
        return false;
    }
}
(lambda (num)
    (if
        (num> num 0)
        true
        false
    )
)

 

 

Example 3:

public static int addTwoNumbers(int a, int b) {
    if (a > 0 && b > 0) {
        return a + b;
    } else {
        return 0;
    }
}
(lambda (a b)
    (if
        (and (num> a 0) (num> b 0))
        (+ a b)
        0
    )
)

 

 

Example 4:

function maxOfThree(a, b, c) {
    if(a >= b && a >= c) {
        return a;
    } else if (b >= a && b >= c) {
        return b;
    } else {
        return c;
    }
}
(lambda (a b c)
    (cond
      ((and (or (num> a b) (num= a b)) (or (num> a c) (num= a c))) a)
      ((and (or (num> b a) (num= b a)) (or (num> b c) (num= b c))) b)
      (else c)
    )
)

 

→ if-else statements가 하나가 아니라 여러개이면 cond 사용

 

 

Example 5:

(lambda (a b c x)
    (+ (+ (* (* a x) x) (* b x)) c)
)
public static int quadraticFunction(int a, int b, int c, int x) {
    return a * x * x + b * x + c;
}

 

 

Example 6:

(lambda (cons1 cons2)
    (cons (- (head cons1) (head cons2)) nil)
)
public static Cons headSubtract(Cons cons1, Cons cons2) {
    return new Cons(cons1.head - cons2.head, null);
}

 

 

 

Translating Function Calls

Example 1:

public static int cube(int x) {
    return (x * x) * x;
}

cube(7); // => 343
((lambda (x) (* (* x x) x)) 7)
 
 
Example 2: 0-arguments method
public static int oneSixNine() {
    return 13 * 13;
}

oneSixNine(); // => 169
(
  (lambda ()
     (* 13 13)
  )
)

 

 

 

Example 3:

public static int arithmetics(int x, boolean flag) {
    if (flag) {
        return x - 3;
    } else {
        return 2 * x;
    }
}

arithmetics(21, false); // => 42
(
  (lambda (x flag)
      (if
         (and flag true)
         (- x 3)
         (* 2 x)
       )
  )
  21 false
)

 

 

→ Function call 하려면 outer parenthesis 만들고 거기다가 arguments 대입.

 

 

Example 4:

function helper(a) {
    if (a > 3) {
        return true;
    } else {
        return false;
    }
}

function doubleOrTriple(a, b) {
    if(b) {
        return 2 * a;
    } else {
        return 3 * a;
    }
}

doubleOrTriple(10, helper(4)); // => 20
(
  (lambda (a b)
      (if
         b
         (* 2 a)
         (* 3 a)
      )
  )
  10 (
      (lambda (a)
         (if
            (num> a 3)
            true
            false
         )
      )
      4
     )
)

 

 

 

Paret  ↔  Java

Example 1:

(let (
      (a 4)
      (b 3)
     )
     (if
         (num> a b)
         true
         false
     )
)
int a = 4;
int b = 3;

if (a > b) {
    return true;
} else {
    return false;
}

 

 

Example 2:

int num1 = 13;
int fortyTwo = 42;

if (num1 < fortyTwo) {
    return num1 + fortyTwo;
} else {
    return fortyTwo - num1;
}
(let (
      (num1 13)
      (fortyTwo 42)
     )
     (if
         (num< num1 fortyTwo)
         (+ num1 fortyTwo)
         (- fortyTwo num1)
     )
)

 

 

Example 3:

(let ((sheep (lambda (x y) (if (num< x y) x y)))
      (bleat 3)
      (baa 5)
     )
     (sheep bleat baa)
)
public int sheep(int x, int y) {
    if (x < y) {
        return x;
    } else {
        return y;
    }
}

int bleat = 3;
int baa = 5;

sheep(bleat, baa); // => 3

 

 

Example 4:

(let ((a 41) (b 42))
     (
       (lambda (x)
           (+ (+ a b) x)
       )
       10
     )
)
int a = 41;
int b = 42;

public int helper(int x) {
    return a + b + x;
}

helper(10); // => 93

 

 

Example 5:

(let (
      (negate (lambda (input) (not input)))
      (var1 true)
      (var2 false)
     )
     (negate (or var1 var2))
)
public boolean negate(boolean input) {
    return !input;
}

boolean var1 = true;
boolean var2 = false;

negate(var1 || var2); // => false

 

 

 

Example 6:

(let ((x 5))
    (+ x 1)
)

 

How can you transform this let expression into a lambda with application, such that both return the same result?

(
  (lambda (x)
     (+ x 1)
  )
  5
)

 

 

Example 7:

Describe a (simple) rule which takes an arbitrary let (with one variable) and turns it into an applied lambda.
Let(LetBind(Id, Value), Body) -> App(Lambda(Id, Body), Value)

→ 즉, lambda 함수를 만들고 variable을 함수의 input으로 한다.

 

 

Example 8:

(let 
    (
        (nand 
            (lambda (x y) 
                (not (and x y))
            )
        )
    )
    (nand false false)
)
public static boolean nand(boolean x, boolean y) {
    return !(x && y);
}

nand(false, false);

 

 

 

Evaluation

Example 1:

(let
    (
        (x 24)
        (f (lambda (y) (- y 14)))
    )
    (f x)
)

Step 1: Variables of let: let has two variables,  x = 24 and f as a function.

Step 2: The body of the let applies the function f on x.

Step 3: Substitute the values of f and x.

((lambda (y) (- y 14)) 24)

Step 4: Replace the argument and evaluate.

(- 24 14)

 

 

Example 2:

(let 
    (
        (arbitraryFunction 
            (lambda 
                (x y z)
                (+ x (- 2 (* y z)))
            )
        )
    )
    (arbitraryFunction 2 3 4)
)

Step 1: The variable arbitraryFunction stores a function. 

Step 2: The body of the let: Applies arbitraryFunction on 3 arguments, 2 3 4.

Step 3: Replace the variable arbitraryFunction with the actual body of the function

(
    (lambda 
        (x y z)
        (+ x (- 2 (* y z)))
    )
    2 3 4
)

Step 4: Replace the arguments 

(+ 2 (- 2 (* 3 4)))

 

 

 

Example 3:

(let
    (
        (a true)
        (b true)
    )
    (let
        (
            (booleanOps
                (lambda 
                    (x y)
                    (and x (or (not y) y))
                )
            )
        )
        (booleanOps a b)
    )
)

Step 1: Outermost let has two variables, a = true, b = true

Step 2: Inner let is a function.

Step 3: Replace the variables

(
    (lambda 
        (x y)
        (and x (or (not y) y))
    )
    true true
)

Step 4: Substitute the parameters with its arguments

(and true (or (not true) true))

 

 

 

Example 4:

(
    (lambda (a b)
        (if (and (num> a 2) (num< b 12))
            (let
                (
                    (square (lambda (x) (* x x)))
                    (q 2)
                )
                (square q)
            )

            (let
                (
                    (r (lambda (y) (+ y y)))
                )
                (r a)
            )
        )
    )
    4 8
)

Step 1: It's a function that takes two parameters, a and b. The arguments are a = 4, b = 8

Step 2: if가 true이므로, 첫번째 let만 evaluate.

Step 3: 첫번째 let은 square 함수고, parameter는 q. 

Step 4:

((lambda (x) (* x x)) 2)

Step 5: 4

 

 

Mandatory Assignment

Example 1:

 

import java.util.NoSuchElementException
import scala.collection.mutable.ListBuffer

object Solution {

    /**
     * Define the function lengths(xs), which returns a list of the lengths of the strings in the list xs.
     * Example: if the input is List("I", "took", "a", "quick", "glimpsing", "at", "bright", "stars"),
     * the output should be List(1, 4, 1, 5, 9, 2, 6, 5).
     *
     * Hint: ListBuffer is a mutable list in Scala that allows appending elements.
     *
     * You MUST use loops and CANNOT use high-order functions.
     */
    def lengths(xs: List[String]): List[Int] = {
        val buf = ListBuffer[Int]()
        for (i <- 0 until xs.length) {
            buf.append(xs(i).length)
        }   
        buf.toList
    }
}

 

설명:

우선 ListBuffer [Int] 형인 ListBuffer를 만든다 (append가 가능하므로).

그리고, for-loop을 이용해서 이 리스트에 문자열 길이를 append하고 마지막에 toList를 함으로서 ListBuffer [Int]를 List [Int]로 바꿔준다.

 

 

Example 2:

import java.util.NoSuchElementException

object Solution {

    /**
     * Define the function lengths(xs), which returns a list of the lengths of the strings in the list xs.
     * Example: if the input is List("I", "took", "a", "quick", "glimpsing", "at", "bright", "stars"),
     * the output should be List(1, 4, 1, 5, 9, 2, 6, 5).
     *
     * You MUST use a high-order function and you CANNOT use loops.
     *
     * After you solve this exercise using high-order function, compare your solution to the one you made
     * in the previous assignment using mutation and loops.
     */
    def lengths(xs: List[String]): List[Int] = {
        xs.map(x => x.length)
    }

    /**
     * Define the function longWords(words), which returns the words that have more than 5 characters.
     *
     * You MUST use a high-order function and you CANNOT use loops.
     */
    def longWords(words: List[String]): List[String] = {
        words.filter(_.length > 5)
    }

    /**
     * Define the function maximum(numbers), which uses foldLeft to return the highest number
     * in the given list of non-negative numbers, and -1 if the list is empty.
     *
     * You MUST use a high-order function and you CANNOT use loops.
     */
    def maximum(numbers: List[Int]): Int = {
        numbers.foldLeft(-1)((acc, num) => if (num > acc) num else acc)
    }
}

 

 

 

Example 3:

import java.util.NoSuchElementException

object Solution {
    /**
      * EXERCISE:
      * Define your own version of map! Loops are prohibited, so use recursion.
      */
    def myMap[A,B](xs: List[A], f: (A => B)): List[B] = xs match {
        case Nil          => throw new RuntimeException("Not yet implemented")
        case head :: tail => throw new RuntimeException("Not yet implemented")
    }


    /**
      * EXERCISE:
      * Take a look at the takeWhile function:
      * https://www.scala-lang.org/api/current/scala/collection/immutable/List.html#takeWhile(p:A=%3EBoolean):List[A]
      *
      * Define the function takeWhileSmallerThanFive, it should take a list and return the first n
      * elements, until the next element (n+1) is greater or equal to 5.
      *
      * Use the takeWhile function!
      * Loops are prohibited!
      */
    def takeWhileSmallerThanFive(xs: List[Int]): List[Int] = {
        throw new RuntimeException("Not yet implemented")
    }

    /**
      * EXERCISE:
      * Define the function dropWhileSmallerThanFive, it should take a list and discard the first n
      * elements, until the next element (n+1) is greater or equal to 5.
      *
      * Again, use one of Scala's built-in list functions.
      * Loops are prohibited!
      */
    def dropWhileSmallerThanFive(xs: List[Int]): List[Int] = {
        throw new RuntimeException("Not yet implemented")
    }

    /**
      * Take a look at the zip function:
      * https://www.scala-lang.org/api/current/scala/collection/immutable/List.html#zip[B](that:scala.collection.GenIterable[B]):List[(A,B)]
      *
      * This function connects two lists by 'zipping' elements into tuples:
      */
    List(1,2,3,4).zip(List(2,4,6,8)) // = List( (1,2), (2,4), (3,6), (4,8) )

    /**
      * EXERCISE:
      * Define zipWith. It should zip two lists, but instead of zipping elements into a tuple,
      * it should use a function to combine two elements.
      *
      * Example: zipWith(List(1, 2, 3),
      *                  List(10, 11, 12),
      *                  (x: Int, y: Int) => x+y)
      * Should return: List(11,13,15)
      *
      * Hint: use map and zip.
      *
      * Loops are prohibited!
      */
    def zipWith[A,B,C](xs: List[A], ys: List[B], f: (A, B) => C): List[C] = {
        throw new RuntimeException("Not yet implemented")
    }
}
import java.util.NoSuchElementException

object Solution {
    /**
      * EXERCISE:
      * Define your own version of map! Loops are prohibited, so use recursion.
      */
    def myMap[A,B](xs: List[A], f: (A => B)): List[B] = xs match {
        case Nil          => Nil
        case head :: tail => f(head) :: myMap(tail, f)
    }


    /**
      * EXERCISE:
      * Take a look at the takeWhile function:
      * https://www.scala-lang.org/api/current/scala/collection/immutable/List.html#takeWhile(p:A=%3EBoolean):List[A]
      *
      * Define the function takeWhileSmallerThanFive, it should take a list and return the first n
      * elements, until the next element (n+1) is greater or equal to 5.
      *
      * Use the takeWhile function!
      * Loops are prohibited!
      */
    def takeWhileSmallerThanFive(xs: List[Int]): List[Int] = {
        xs.takeWhile(_ < 5)
    }

    /**
      * EXERCISE:
      * Define the function dropWhileSmallerThanFive, it should take a list and discard the first n
      * elements, until the next element (n+1) is greater or equal to 5.
      *
      * Again, use one of Scala's built-in list functions.
      * Loops are prohibited!
      */
    def dropWhileSmallerThanFive(xs: List[Int]): List[Int] = {
        xs.dropWhile(_ < 5)
    }

    /**
      * Take a look at the zip function:
      * https://www.scala-lang.org/api/current/scala/collection/immutable/List.html#zip[B](that:scala.collection.GenIterable[B]):List[(A,B)]
      *
      * This function connects two lists by 'zipping' elements into tuples:
      */
    List(1,2,3,4).zip(List(2,4,6,8)) // = List( (1,2), (2,4), (3,6), (4,8) )

    /**
      * EXERCISE:
      * Define zipWith. It should zip two lists, but instead of zipping elements into a tuple,
      * it should use a function to combine two elements.
      *
      * Example: zipWith(List(1, 2, 3),
      *                  List(10, 11, 12),
      *                  (x: Int, y: Int) => x+y)
      * Should return: List(11,13,15)
      *
      * Hint: use map and zip.
      *
      * Loops are prohibited!
      */
    def zipWith[A,B,C](xs: List[A], ys: List[B], f: (A, B) => C): List[C] = {
        val zipped = xs.zip(ys)
        zipped.map{ case (a, b) => f(a, b)}
    }
}

 

 

 

중요

기본 Higher-Order Functions

map(f): Returns a new list where the function f has been applied to all elements of the list. 

Example: List(1, 2, 3).map(_ + 1) returns List(2, 3, 4).

 

filter(p): Returns a new list, with only the elements that satisfy the predicate p. 

Example: List(1, 2, 3).filter(_ > 2) returns List(3).

 

foldLeft(start)(f): Combines all elements into one return value. It first applies the function f to the start value and the first element of the list, the applies f to the result of that and the second element of the list, and so on. This function goes from left to right through the list.

Example: List(3, 5, 7).fold(0)(_ + _) returns the sum of the list: 15. (It first calculates 0 + 3 = 3, then 3 + 5 = 8, then 8 + 7 = 15.)

 

 

 

반응형

'학교 > CPL' 카테고리의 다른 글

Lecture 6: Environments and Scoping  (0) 2024.03.29
Lecture 5: Functions, Substitution and Environments  (1) 2024.03.28
Lecture 4: Functions  (1) 2024.03.27
CPL 1-2: Semantics and Transformations  (0) 2024.03.26
Lecture 3: Semantics & Transformations  (1) 2024.03.26

댓글