# embedding_metadata

key: chroma:document
string_value:
 system   multiple-dwelling   multiplicand   multiplication by Russian peasant method   multiplier        primitive constraint       selector   Munro, Ian   mutable data objects , see also  queue; table       implemented with assignment       list structure       pairs       procedural representation of       shared data   mutator   mutex   mutual exclusion   mystery   name, see also  local name; variable; local variable       encapsulated       of a formal parameter       of a procedure  named let (special form)   naming       of computational objects       of procedures   naming conventions       ! for assignment and mutation       ? for predicates   native language of machine   natural language       parsing, see parsing natural language       quotation in   needed registers, see instruction sequence   needs-register?   negate   nested applications of car and cdr  nested combinations   nested definitions, see internal definition   nested mappings, see mapping   new register   new-cars  register   new-cdrs 


key: chroma:document
string_value:
 register   new-withdraw   newline  (primitive procedure) , [2]  Newton's method       for cube roots       for dif ferentiable functions       half-interval method vs.       for square roots , [2], [3]  newton-transform   newtons-method   next  (linkage descriptor)   next-to  (rules)   nil       dispensing with       as empty list       as end-of-list marker       as ordinary variable in Scheme  no-more-exps?   no-operands?   node of a tree   non-computable   non-strict   nondeterminism, in behavior of concurrent programs , [2]  nondeterministic choice point   nondeterministic computing   nondeterministic evaluator       order of operand evaluation   nondeterministic programming vs. Scheme programming , [2], [3], [4]  nondeterministic programs       logic puzzles       pairs with prime sums       parsing natural language       Pythagorean triples , [2], [3]  normal-order evaluation       applicative order vs. , [2], [3]      delayed evaluation and       in explicit-control evaluator       of if  normal-order


key: chroma:document
string_value:
 evaluator , see lazy evaluator  not (primitive procedure)   not (query language) , [2]      evaluation of , [2], [3]  notation in this book       italic symbols in expression syntax       slanted characters for interpreter response   nouns   nth root, as fixed point   null?  (primitive procedure)       implemented with typed pointers   number theory   number(s)       comparison of       decimal point in       equality of , [2], [3]     in generic arithmetic system       implementation dependencies       integer vs. real number       integer , exact       in Lisp       rational number   number?  (primitive procedure)       data types and       implemented with typed pointers   numer , [2]      axiom for       reducing to lowest terms   numerical analysis   numerical analyst   numerical data   obarray   object program   object(s)       benefits of modeling with       with time-varying state   object-oriented programming languages   old register   oldcr  register   ones  (infinite stream)       lazy-list versio


key: chroma:document
string_value:
n   op (in register machine)       simulating   open coding of primitives , [2]  operands   operands of a combination   operation       cross-type       generic       in register machine   operation-and-type table      assignment needed for       implementing   operation-exp   operation-exp-op   operation-exp-operands   operator   operator of a combination       combination as       compound expression as       lambda  expression as   optimality       of Horner's rule       of Huf fman code   or (query language)       evaluation of , [2]  or (special form)       evaluation of       why a special form       with no subexpressions   or-gate       or-gate , [2]  order , [2]  order notation   order of evaluation       assignment and       implementation-dependent       in compiler       in explicit-control evaluator       in metacircular evaluator       in Scheme   order of events       decoupling apparent from actual       indeterminacy in concurrent systems   order of growth       linear iterative process      


key: chroma:document
string_value:
 linear recursive process       logarithmic      tree-recursive process   order of subexpression evaluation, see order of evaluation   ordered-list representation of sets   ordinary numbers (in generic arithmetic system)   origin-frame   Ostrowski, A. M.   outranked-by  (rule) , [2]   P operation on semaphore   package       complex-number       polar representation       polynomial       rational-number       rectangular representation       Scheme-number   painter(s)       higher -order operations       operations       represented as procedures       transforming and combining   pair(s)       axiomatic definition of       box-and-pointer notation for       infinite stream of       lazy      mutable       procedural representation of , [2], [3]      represented using vectors       used to represent sequence       used to represent tree   pair?  (primitive procedure)       implemented with typed pointers   pairs   Pan, V . Y.  parallel-execute  parallel-instruction-sequences   parallelism, see concurrency   


key: chroma:document
string_value:
parameter , see formal parameters   parameter passing, see call-by-name ar gument passing; call-by-need argument passing   parentheses       delimiting combination       delimiting cond  clauses       in procedure definition   parse   parse-...   parsing natural language       real language understanding vs. toy parser   partial-sums   Pascal       lack of higher -order procedures       recursive procedures       restrictions on compound data       weakness in handling compound objects   Pascal, Blaise   Pascal's triangle   password-protected bank account   pattern   pattern matching       implementation       unification vs. , [2]  pattern variable       representation of , [2]  pattern-match   pc register   perform  (in register machine)       simulating   perform-action   Perlis, Alan J. , [2]      quips , [2]  permutations of a set       permutations  Phillips, Hubert   π (pi)       approximation with half-interval method       approximation with Monte Carlo integration , [2]      Cesàro estimate for , [2


key: chroma:document
string_value:
]      Leibniz's series for , [2]      stream of approximations       Wallis's formula for   pi-stream   pi-sum       with higher -order procedures       with lambda   picture language   Pingala, Áchárya   pipelining   Pitman, Kent M.   Planner   point, represented as a pair   pointer       in box-and-pointer notation       typed   polar  package   polar?   poly  polynomial  package   polynomial arithmetic       addition       division       Euclid's Algorithm       greatest common divisor , [2]      interfaced to generic arithmetic system       multiplication       probabilistic algorithm for GCD       rational functions       subtraction   polynomial(s)       canonical form      dense       evaluating with Horner's rule       hierarchy of types       indeterminate of       sparse       univariate   pop  Portable Standard Lisp   porting a language   power series, as stream       adding       dividing       integrating       multiplying   PowerPC   predicate       of cond  clause       of if      naming conve


key: chroma:document
string_value:
ntion for   prefix code   prefix notation       infix notation vs.   prepositions   preserving , [2], [3], [4]  pretty-printing   prime number(s)       cryptography and       Eratosthenes's sieve for       Fermat test for       infinite stream of, see primes        Miller -Rabin test for       testing for   prime-sum-pair   prime-sum-pairs       infinite stream   prime? , [2]  primes  (infinite stream)      implicit definition   primitive constraints   primitive expression       evaluation of       name of primitive procedure       name of variable       number   primitive procedures (those marked ns are not in the IEEE Scheme standard)       *      +      -, [2]      /      <      =      >      apply       atan       car      cdr      cons       cos      display       eq?      error  (ns)      eval  (ns)      list       log      max      min      newline       not      null?       number?       pair?       quotient       random  (ns), [2]     read       remainder       round       runtime  (ns)      set-car!


key: chroma:document
string_value:
       set-cdr!       sin      symbol?       vector-ref       vector-set!   primitive query , see simple query   primitive-apply   primitive-implementation   primitive-procedure-names   primitive-procedure-objects   primitive-procedure? , [2] principle of least commitment   print  operation in register machine   print-point   print-queue   print-rat   print-result       monitored-stack version   print-stack-statistics  operation in register machine   printing, primitives for   probabilistic algorithm , [2], [3]  probe        in constraint system       in digital-circuit simulator   proc  register   procedural abstraction   procedural representation of data       mutable data   procedure , [2]      anonymous       arbitrary number of ar guments , [2]      as argument      as black box       body of       compound       creating with define       creating with lambda , [2], [3]      as data       definition of       first-class in Lisp       formal parameters of       as general method       generic , [2]      


key: chroma:document
string_value:
higher -order , see higher -order procedure       implicit begin  in body of       mathematical function vs.       memoized       monitored       name of       naming (with define )      as pattern for local evolution of a process       as returned value       returning multiple values       scope of formal parameters       special form vs. , [2]  procedure application       combination denoting       environment model of       substitution model of, see substitution model of procedure application   procedure-body   procedure-environment   procedure-parameters   process       iterative       linear iterative       linear recursive       local evolution of       order of growth of       recursive      resources required by       shape of       tree-recursive   product       as accumulation   product?   program       as abstract machine       comments in       as data       incremental development of     structure of , [2], [3], see also  abstraction barriers       structured with subroutines   program counter 


key: chroma:document
string_value:
  programming       data-directed, see data-directed programming       demand-driven       elements of       functional, see functional programming       imperative       odious style   programming language       design of       functional       logic       object-oriented       strongly typed       very high-level   Prolog , [2]  prompt-for-input   prompts       explicit-control evaluator       lazy evaluator       metacircular evaluator       nondeterministic evaluator       query interpreter   propagate  propagation of constraints   proving programs correct   pseudo-random sequence   pseudodivision of polynomials   pseudoremainder of polynomials   push   put, [2]  puzzles       eight-queens puzzle , [2]      logic puzzles   Pythagorean triples       with nondeterministic programs , [2], [3]      with streams   qeval , [2]  quantum mechanics   quasiquote   queens   query , see also  simple query; compound query   query interpreter       adding rule or assertion       compound query , see compound query     


key: chroma:document
string_value:
  data base       driver loop , [2]      environment structure in       frame , [2]      improvements to , [2], [3]      infinite loops , [2]      instantiation       Lisp interpreter vs. , [2], [3]      overview       pattern matching , [2]      pattern-variable representation , [2]      problems with not and lisp-value , [2]      query evaluator , [2]      rule, see rule      simple query , see simple query       stream operations       streams of frames , [2]      syntax of query language       unification , [2]  query language , [2]      abstraction in       compound query , see compound query       data base       equality testing in       extensions to , [2]      logical deductions       mathematical logic vs.       rule, see rule       simple query , see simple query   query-driver-loop   question mark, in predicate names   queue       double-ended       front of       operations on       procedural implementation of       rear of       in simulation agenda   quotation       of character strings       


key: chroma:document
string_value:
of Lisp data objects       in natural language   quotation mark, single vs. double   quote  (special form)       read  and, [2]  quoted?   quotient  (primitive procedure)   Rabin, Michael O.   radicand  Ramanujan numbers   Ramanujan, Srinivasa   rand       with reset   random  (primitive procedure)       assignment needed for       MIT Scheme   random-in-range   random-number generator , [2]      in Monte Carlo simulation       in primality testing       with reset       with reset, stream version   random-numbers  (infinite stream)   Raphael, Bertram   rational  package   rational function       reducing to lowest terms   rational number(s)       arithmetic operations on       in MIT Scheme       printing       reducing to lowest terms , [2]      represented as pairs   rational-number arithmetic       interfaced to generic arithmetic system       need for compound data   Raymond, Eric , [2]  RC circuit   read  (primitive procedure)       dotted-tail notation handling by       macro characters   read  operati


key: chroma:document
string_value:
on in register machine   read-eval-print loop , see also  driver loop   read-eval-print-loop   reader macro character   real number  real-part        data-directed       polar representation       rectangular representation       with tagged data   real-part-polar   real-part-rectangular   rear-ptr   receive  procedure   record, in a data base rectangle, representing   rectangular  package   rectangular?   recursion       data-directed       expressing complicated process       in rules       in working with trees   recursion equations   recursion theory   recursive procedure       recursive procedure definition       recursive process vs.       specifying without define   recursive process       iterative process vs. , [2], [3], [4]      linear , [2]      recursive procedure vs.       register machine for       tree, [2]  red-black tree   reducing to lowest terms , [2], [3]  Rees, Jonathan A. , [2]  referential transparency   reg (in register machine)       simulating   register machine      actions       co


key: chroma:document
string_value:
ntroller       controller diagram       data paths       data-path diagram       design of       language for describing       monitoring performance       simulator       stack       subroutine       test operation   register table, in simulator   register(s)       representing       tracing   register-exp   register-exp-reg   register -machine language       assign , [2]      branch , [2]      const , [2], [3]      entry point       goto , [2]      instructions , [2]      label       label , [2]      op, [2]      perform , [2]      reg, [2]      restore , [2]      save , [2]      test , [2]  register -machine simulator   registers-modified   registers-needed   relations, computing in terms of , [2] relatively prime   relativity , theory of   release a mutex   remainder  (primitive procedure)   remainder modulo n  remainder-terms   remove   remove-first-agenda-item! , [2] require       as a special form   reserved words , [2]  resistance       formula for parallel resistors , [2]      tolerance of resistors 


key: chroma:document
string_value:
  resolution principle   resolution, Horn-clause   rest-exps   rest-operands   rest-segments   rest-terms , [2]  restore  (in register machine) , [2]      implementing       simulating   return  (linkage descriptor)   returning multiple values   Reuter , Andreas   reverse       as folding       rules   Rhind Papyrus   right-branch , [2]  right-split   ripple-carry adder   Rivest, Ronald L. , [2]  RLC circuit   Robinson, J. A.   robustness  rock songs, 1950s   Rogers, W illiam Barton   root  register   roots of equation, see half-interval method; Newton's method   rotate90   round  (primitive procedure)   roundof f error , [2]  Rozas, Guillermo Juan   RSA algorithm   rule (query language)       applying , [2], [3]      without body , [2], [3]  Runkle, John Daniel   runtime  (primitive procedure)   Russian peasant method of multiplication   same  (rule)   same-variable? , [2]  sameness and change       meaning of       shared data and   satisfy a compound query   satisfy a pattern (simple query)   save  (in reg


key: chroma:document
string_value:
ister machine) , [2]      implementing       simulating   scale-list , [2], [3]  scale-stream   scale-tree , [2]  scale-vect   scan  register   scan-out-defines   scanning out internal definitions       in compiler , [2]  Scheme       history of  Scheme chip , [2]  scheme-number  package   scheme-number->complex   scheme-number->scheme-number   Schmidt, Eric   scope of a variable , see also  lexical scoping       internal define       in let     procedure's formal parameters   search       of binary tree       depth-first       systematic   search   secretary , importance of   segment-queue   segment-time   segments   segments->painter   selector       as abstraction barrier       generic , [2]  self-evaluating expression   self-evaluating?   semaphore       of size n  semicolon       comment introduced by   separator code   sequence accelerator   sequence of expressions       in consequent of cond       in procedure body   sequence(s)       as conventional interface       as source of modularity       operat


key: chroma:document
string_value:
ions on      represented by pairs   sequence->exp   serialized-exchange       with deadlock avoidance   serializer       implementing       with multiple shared resources   series, summation of       accelerating sequence of approximations       with streams   set      set            (special form), see also  assignment       data base as       operations on       permutations of       represented as binary tree       represented as ordered list     represented as unordered list     subsets of   set!  (special form)       environment model of       value of   set-car!  (primitive procedure)       implemented with vectors       procedural implementation of       value of   set-cdr!  (primitive procedure)       implemented with vectors       procedural implementation of       value of   set-contents!   set-current-time!   set-front-ptr!   set-instruction-execution-proc!   set-rear-ptr!   set-register-contents! , [2] set-segments!   set-signal! , [2]  set-value! , [2]  set-variable-value! , [2]  setup-environmen


key: chroma:document
string_value:
t   shadow a binding   Shamir , Adi   shape of a process   shared data   shared resources   shared state   shrink-to-upper-right   Shrobe, Howard E.   side-ef fect bug   sieve of Eratosthenes       sieve   sum (sigma) notation   signal processing       smoothing a function       smoothing a signal , [2]      stream model of       zero crossings of a signal , [2], [3]  signal, digital   signal-error   signal-flow diagram , [2]  signal-processing view of computation   simple query       processing , [2], [3], [4]  simple-query   simplification of algebraic expressions Simpson's Rule for numerical integration   simulation       of digital circuit, see digital-circuit simulation       event-driven       as machine-design tool       for monitoring performance of register machine       Monte Carlo, see Monte Carlo simulation      of register machine, see register -machine simulator   sin (primitive procedure)   sine       approximation for small angle       power series for   singleton-stream   SKETCHP AD  smallest


key: chroma:document
string_value:
-divisor       more ef ficient version   Smalltalk   smoothing a function   smoothing a signal , [2]  snarf   Solar System's chaotic dynamics   Solomonof f, Ray   solve  differential equation , [2]      lazy-list version       with scanned-out definitions   solving equation, see half-interval method; Newton's method; solve    source language   source program   Spafford, Eugene H.   sparse polynomial   special form       as derived expression in evaluator       need for       procedure vs. , [2]  special forms (those marked ns are not in the IEEE Scheme standard)       and      begin       cond       cons-stream  (ns)      define , [2]      delay  (ns)      if      lambda       let     let*       letrec       named let      or      quote       set!   split   sqrt       block structured       in environment model       as fixed point , [2], [3], [4]      as iterative improvement       with Newton's method , [2]      register machine for       as stream limit   sqrt-stream   square       in environment model   s


key: chroma:document
string_value:
quare root , see also  sqrt        stream of approximations   square-limit , [2]  square-of-four   squarer  (constraint) , [2]  squash-inwards   stack       framed       for recursion in register machine       representing , [2]  stack allocation and tail recursion   stack-inst-reg-name   Stallman, Richard M. , [2]  start  register machine , [2]  start-eceval   start-segment , [2]  state       local, see local state       shared      vanishes in stream formulation   state variable , [2]      local   statements, see instruction sequence   statements   Steele, Guy Lewis Jr ., [2], [3], [4], [5], [6]  stop-and-copy garbage collector   Stoy, Joseph E. , [2], [3]  Strachey , Christopher   stratified design   stream(s) , [2]      delayed evaluation and       empty       implemented as delayed lists       implemented as lazy lists       implicit definition       infinite, see infinite streams       used in query interpreter , [2] stream-append   stream-append-delayed   stream-car , [2]  stream-cdr , [2]  stream-enum


key: chroma:document
string_value:
erate-interval   stream-filter   stream-flatmap , [2]  stream-for-each   stream-limit   stream-map       with multiple ar guments   stream-null?       in MIT Scheme   stream-ref   stream-withdraw   strict   string, see character string   strongly typed language   sub (generic)  sub-complex   sub-interval   sub-rat   sub-vect   subroutine in register machine   subsets  of a set   substitution model of procedure application , [2]      inadequacy of       shape of process   subtype       multiple   success continuation (nondeterministic evaluator) , [2]  successive squaring   sum      as accumulation       iterative version   sum-cubes       with higher -order procedures   sum-integers       with higher -order procedures   sum-odd-squares , [2]  sum-of-squares       in environment model   sum-primes , [2]  sum?   summation of a series       with streams   supertype       multiple   Sussman, Gerald Jay , [2], [3], [4], [5], [6], [7]  Sussman, Julie Esther Mazel, nieces of   Sutherland, Ivan   symbol(s)       equa


key: chroma:document
string_value:
lity of       interning       quotation of       representation of      uniqueness of   symbol-leaf   symbol?  (primitive procedure)       data types and       implemented with typed pointers   symbolic algebra   symbolic dif ferentiation , [2]  symbolic expression , see also  symbol(s)   symbols   SYNC synchronization, see concurrency   syntactic analysis, separated from execution       in metacircular evaluator       in register -machine simulator , [2]  syntactic sugar       define       let as      looping constructs as       procedure vs. data as   syntax, see also  special forms       abstract, see abstract syntax       of expressions, describing       of a programming language   syntax interface   systematic search   #t  table       backbone of       for coercion       for data-directed programming       local       n-dimensional       one-dimensional       operation-and-type, see operation-and-type table       represented as binary tree vs. unordered list      testing equality of keys       two-dimens


key: chroma:document
string_value:
ional       used in simulation agenda       used to store computed values   tableau   tabulation , [2]  tack-on-instruction-sequence   tagged architecture   tagged data , [2]  tagged-list?   tail recursion       compiler and       environment model of evaluation and       explicit-control evaluator and , [2], [3]      garbage collection and       metacircular evaluator and       in Scheme   tail-recursive evaluator   tangent       as continued fraction       power series for   target register   Technological University of Eindhoven   Teitelman, W arren   term list of polynomial       representing   term-list   terminal node of a tree   test  (in register machine)       simulating   test operation in register machine   test-and-set! , [2]  test-condition   text-of-quotation   Thatcher , James W .  THE Multiprogramming System   the-cars       register , [2]      vector   the-cdrs        register , [2]      vector   the-empty-stream       in MIT Scheme   the-empty-termlist , [2]  the-global-environment , [2]  th


key: chroma:document
string_value:
eorem proving (automatic)   θ (f(n)) (theta of f(n))  thunk       call-by-name       call-by-need       forcing       implementation of       origin of name   time       assignment and       communication and       in concurrent systems       functional programming and       in nondeterministic computing , [2]      purpose of   time segment, in agenda   time slicing   timed-prime-test   timing diagram   TK!Solver   tower of types   tracing       instruction execution       register assignment   transform-painter   transparency , referential   transpose  a matrix   tree      B-tree       binary , see also  binary tree       combination viewed as       counting leaves of       enumerating leaves of       fringe of       Huffman       lazy      mapping over       red-black       represented as pairs       reversing at all levels   tree accumulation   tree->list...   tree-map   tree-recursive process       order of growth   trigonometric relations   true  true   true?   truncation error   truth maintenance   try-


key: chroma:document
string_value:
again   Turing machine   Turing, Alan M. , [2]  Turner , David , [2], [3]  type field   type tag , [2]      two-level   type(s)       cross-type operations       dispatching on       hierarchy in symbolic algebra       hierarchy of       lowering , [2]      multiple subtype and supertype      raising , [2]      subtype       supertype       tower of   type-inferencing mechanism   type-tag       using Scheme data types   typed pointer   typing input expressions   unbound variable   unev  register   unification       discovery of algorithm       implementation       pattern matching vs. , [2]  unify-match   union-set       binary-tree representation       ordered-list representation       unordered-list representation   unique  (query language)   unique-pairs   unit square   univariate polynomial   universal machine       explicit-control evaluator as     general-purpose computer as   University of California at Berkeley   University of Edinbur gh  University of Marseille   UNIX , [2]  unknown-expression-type  


key: chroma:document
string_value:
 unknown-procedure-type   unordered-list representation of sets   unspecified values      define       display       if without alternative       newline       set!       set-car!       set-cdr!   up-split   update-insts!   upper-bound   upward compatibility   user-initial-environment  (MIT Scheme)   user-print       modified for compiled code   V operation on semaphore   val register   value       of a combination       of an expression , see also  unspecified values   value-proc   variable , see also  local variable       bound       free      scope of , see also  scope of a variable       unbound       value of , [2]  variable   variable-length code   variable? , [2]  vector (data structure)   vector (mathematical)       operations on , [2]      in picture-language frame       represented as pair       represented as sequence  vector-ref  (primitive procedure)   vector-set!  (primitive procedure)   Venus  verbs   very high-level language   Wadler , Philip   Wadsworth, Christopher   Wagner , Eric G.   Walke


key: chroma:document
string_value:
r , Francis Amasa   Wallis, John   Wand, Mitchell , [2]  Waters, Richard C.   weight   weight-leaf   Weyl, Hermann   “what is” vs. “how to” description, see declarative vs. imperative knowledge   wheel  (rule) , [2]  width   width of an interval   Wilde, Oscar (Perlis's paraphrase of)   Wiles, Andrew   Winograd, T erry  Winston, Patrick Henry , [2]  wire, in digital circuit   Wisdom, Jack   Wise, David S.   wishful thinking , [2]  withdraw       problems in concurrent system   without-interrupts   world line of a particle , [2]  Wright, E. M.   Wright, Jesse B.  xcor-vect   Xerox Palo Alto Research Center , [2]   Y operator   ycor-vect   Yochelson, Jerome C.   Zabih, Ramin   zero crossings of a signal , [2], [3]  zero test (generic)     for polynomials   Zetalisp   Zilles, Stephen N.   Zippel, Richard E.  Contents     Foreword       Preface to the Second Edition       Preface to the First Edition       Acknowledgments       1  Building Abstractions with Pr ocedur es          1.1  The Elements of Programming  


key: chroma:document
string_value:
             1.1.1  Expressions               1.1.2  Naming and the Environment               1.1.3  Evaluating Combinations               1.1.4  Compound Procedures               1.1.5  The Substitution Model for Procedure Application               1.1.6  Conditional Expressions and Predicates               1.1.7  Example: Square Roots by Newton's Method               1.1.8  Procedures as Black-Box Abstractions           1.2  Procedures and the Processes They Generate               1.2.1  Linear Recursion and Iteration               1.2.2  Tree Recursion               1.2.3  Orders of Growth               1.2.4  Exponentiation               1.2.5  Greatest Common Divisors               1.2.6  Example: T esting for Primality           1.3  Formulating Abstractions with Higher -Order Procedures               1.3.1  Procedures as Ar guments               1.3.2  Constructing Procedures Using Lambda               1.3.3  Procedures as General Methods               1.3.4  Procedures as Returned V alues      2  Buil


key: chroma:document
string_value:
ding Abstractions with Data           2.1  Introduction to Data Abstraction               2.1.1  Example: Arithmetic Operations for Rational Numbers               2.1.2  Abstraction Barriers               2.1.3  What Is Meant by Data?               2.1.4  Extended Exercise: Interval Arithmetic           2.2  Hierarchical Data and the Closure Property               2.2.1  Representing Sequences               2.2.2  Hierarchical Structures               2.2.3  Sequences as Conventional Interfaces               2.2.4  Example: A Picture Language           2.3  Symbolic Data               2.3.1  Quotation               2.3.2  Example: Symbolic Dif ferentiation               2.3.3  Example: Representing Sets               2.3.4  Example: Huf fman Encoding T rees          2.4  Multiple Representations for Abstract Data               2.4.1  Representations for Complex Numbers               2.4.2  Tagged data               2.4.3  Data-Directed Programming and Additivity         2.5  Systems with Generic Operations   


key: chroma:document
string_value:
            2.5.1  Generic Arithmetic Operations               2.5.2  Combining Data of Dif ferent T ypes              2.5.3  Example: Symbolic Algebra       3  Modularity , Objects, and State           3.1  Assignment and Local State               3.1.1  Local State V ariables               3.1.2  The Benefits of Introducing Assignment               3.1.3  The Costs of Introducing Assignment           3.2  The Environment Model of Evaluation               3.2.1  The Rules for Evaluation               3.2.2  Applying Simple Procedures               3.2.3  Frames as the Repository of Local State               3.2.4  Internal Definitions           3.3  Modeling with Mutable Data               3.3.1  Mutable List Structure              3.3.2  Representing Queues               3.3.3  Representing T ables               3.3.4  A Simulator for Digital Circuits               3.3.5  Propagation of Constraints           3.4  Concurrency: T ime Is of the Essence               3.4.1  The Nature of T ime in Concurrent Sys


key: chroma:document
string_value:
tems               3.4.2  Mechanisms for Controlling Concurrency           3.5  Streams               3.5.1  Streams Are Delayed Lists               3.5.2  Infinite Streams               3.5.3  Exploiting the Stream Paradigm               3.5.4  Streams and Delayed Evaluation               3.5.5  Modularity of Functional Programs and Modularity of Objects       4  Metalinguistic Abstraction           4.1  The Metacircular Evaluator               4.1.1  The Core of the Evaluator               4.1.2  Representing Expressions               4.1.3  Evaluator Data Structures               4.1.4  Running the Evaluator as a Program               4.1.5  Data as Programs               4.1.6  Internal Definitions               4.1.7  Separating Syntactic Analysis from Execution           4.2  Variations on a Scheme – Lazy Evaluation               4.2.1  Normal Order and Applicative Order               4.2.2  An Interpreter with Lazy Evaluation               4.2.3  Streams as Lazy Lists           4.3  Variations on a Sch


key: chroma:document
string_value:
eme – Nondeterministic Computing               4.3.1  Amb and Search               4.3.2  Examples of Nondeterministic Programs               4.3.3  Implementing the Amb Evaluator           4.4  Logic Programming               4.4.1  Deductive Information Retrieval               4.4.2  How the Query System W orks              4.4.3  Is Logic Programming Mathematical Logic?               4.4.4  Implementing the Query System      5  Computing with Register Machines           5.1  Designing Register Machines               5.1.1  A Language for Describing Register Machines               5.1.2  Abstraction in Machine Design               5.1.3  Subroutines               5.1.4  Using a Stack to Implement Recursion               5.1.5  Instruction Summary           5.2  A Register -Machine Simulator               5.2.1  The Machine Model               5.2.2  The Assembler               5.2.3  Generating Execution Procedures for Instructions               5.2.4  Monitoring Machine Performance           5.3  Storage A


key: chroma:document
string_value:
llocation and Garbage Collection               5.3.1  Memory as V ectors               5.3.2  Maintaining the Illusion of Infinite Memory           5.4  The Explicit-Control Evaluator               5.4.1  The Core of the Explicit-Control Evaluator               5.4.2  Sequence Evaluation and T ail Recursion               5.4.3  Conditionals, Assignments, and Definitions               5.4.4  Running the Evaluator           5.5  Compilation               5.5.1  Structure of the Compiler               5.5.2  Compiling Expressions               5.5.3  Compiling Combinations               5.5.4  Combining Instruction Sequences               5.5.5  An Example of Compiled Code               5.5.6  Lexical Addressing               5.5.7  Interfacing Compiled Code to the Evaluator       Refer ences       List of Exer cises       Index  .........................................................................................................................................................................................


key: chroma:document
string_value:
The transcription of the contents in the image is as follows:

---

CSC 335 Sections M and R  
Classes 3 and 4  
February 1 and February 6 2024

; functions are values, too Scheme has first-class functions  
(lambda (x) (* x x)) lambda causes Scheme to create a  
   ↑        ↑
special closure You might enjoy  
form searching  
reserved the language  
word Closure  

That is, a function along with certain environmental information  
For example, in response to  
(define sq lambda (x) (* x x))  

The system augments its starting environment as follows →  
    ↓         →
global namespace G  

---


key: chroma:document
string_value:
The image contains notes discussing concepts related to programming in the Scheme programming language. Here’s the transcription:

```
starting environment
for Scheme – everything
predefined in
Scheme is
installed here

sg --> ?

Step 1: install sg in G
(but we don’t have a value 
for sg yet, hence the
question mark)

Step 2: evaluate the lambda

sg --> ?

closure
created by
(lambda(x)(+ x x))

environment part
of closure

code part
of closure

params (x)
body (+ x x)
```


key: chroma:document
string_value:
The contents of the image include handwritten text and diagrams related to programming and functions. Here is the transcription:

---
Step 3: bind sq to the closure

The closure is a scheme object (much as the number 1 is a scheme object).

With this in place, what happens when we ask the system to evaluate `(sq 4)`?

Not `sq(4)`.

```
sq      .                  .
param -> (x)
body  -> (x * x)
```

Scheme has dynamic typing.

Function name `(sq 4) `<- argument - i.e., this is the actual parameter for the def.-given, scheme absolutely insists on exactly ONE argument.
---

Note: The image also contains various arrows and a box diagram linking the aspects of the function to illustrate the closure binding process.


key: chroma:document
string_value:
CSc 335 Sections M and R
Classes 3 and 4
February 1 and February 6 2024

; functions are values, too
Scheme has first-class functions
(lambda (x) (* x x))
lambda causes Scheme to create a closure
special form
reserved word
You might enjoy searching the language closure

that is, a function, along with certain environmental information

For example, in response to
define sq
lambda (x) (* x x)

The system augments its starting environment as follows ->
global namespace -> G


key: chroma:document
string_value:
starting environment - everything pre-defined in scheme is installed here

- - - - - - - - - - - - - - - -

sq ----> ?

t ---------->

Step 1: install sq in G
(but we don't have a value for sq yet, hence the question mark)

Step 2: evaluate the lambda
closure created by (lambda (x) (* x x))

environment part of closure

-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --

sq ----> ?

-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --

params (x)
body (* x x)

-- -- -- -- -- -- -- -- -- -- -- -- --
code part of closure


key: chroma:document
string_value:
Step 3: bind sq to the closure

The closure is a Scheme object (much as the number 1 is a Scheme object).

sq

param -> (x)
body -> (* x x)

With this in place, what happens when we ask the system to evaluate (sq 4)? Not sq(4)!

function
name

Scheme has dynamic typing

for the def. given, Scheme absolutely insists on exactly ONE argument.


key: chroma:document
string_value:
G
an
environment
is a
sequence
of
frames
global frame
When
(f sq 4) is requested
The system opens a new
frame
x: 4
or parent
frame
system sees that the frame
associated with sq is G ,
so it makes G the containing
frame of this new frame
3. In the new frame, the formal
parameter x is bound to the
actual parameter (in this case, 4)
We say 'x has value 4 in this frame'
4. Next, scheme evaluates the
body (x x x) in the new


key: chroma:document
string_value:
frame

This new frame is memory allocated from the heap

x; A

(* x x x)

What does it mean to evaluate (* x x x) in the new frame?
(i.e., in the env. formed by the new frame and S9 as its parent.

We know from the scheme evaluation rule that the system needs to eval. x and x . . . to evaluate x you can think of the


key: chroma:document
string_value:
system starting to look in the closest frame. But x is not defined in that frame (only x is), so the system goes to the parent frame and looks there for the definition of x.

Since in this case the parent frame is G, the scope of x is found. So x is evaluated to the system mft.

What about x? Again, the system starts in the closest


key: chroma:document
string_value:
is returned to the heap.

How do we tell whether a storage is still alive? Storage is live if it is pointed at (even indirectly) from the initial environment.

Not alive, because no pointer from g
Alive because pointed at by sq


key: chroma:document
string_value:
When the entire evaluation is done, the situation is

No concerns about memory leakage, as GC takes care of the problem.

This evaluation model is described in Ch. 3 of A&S — EXCEPT they have introduced assignment by them, they call it the environment model of evaluation.


key: chroma:document
string_value:
We can use this model to explain
Why (sq 5) does not change
x if x has previously been
defined.
Suppose (define x 7)
(sq 5)
and subseq. ask for x - you
will see that it is still 7. Why?


key: chroma:document
string_value:
Here's the situation after x and sq have been defined. x : 17. This binding is shadowed by the local binding. After sq, x : 5. Again, x is found in G and x is found in the new frame. The system takes the first value of x that it finds as it works up the linked list of frames.


key: chroma:document
string_value:
After computing 25, the new frame is garbage - and we're left with 
 x : 17 
  
 and of course when now we ask for the value of x, we get 17. 
 (the 5 is gone for good)


key: chroma:document
string_value:
; some names are local, while others are global
(define x 3)
(define (add-x y) (+ x y))
x is free in the body of add-x
Abelson-Sussman format, which is under the hood — the same as the lambda form
What happens when we have these definitions, and then one for (add-x 6) ?

(add-x y)

(closure always goes
to the frame in
which the definition
closure
was created)

x: 3

(add-x)

y: 6
(+ x y)

(+ x y)

(+ x y)

(? system
+ 3
+ 6
~ 6)


key: chroma:document
string_value:
Student Question: What if add-x were defined
(define (add-x x y)
  (+ x y))
Consider
(add-x 4 5)

x: 3
add-x
x: 4
y: 5
(+ x y)
 9
 4 5

Note that you may not call this new version with just a single argument.


key: chroma:document
string_value:
What about
(define x 3)
(add-x x 4)
?
In this case, the system would eval x to 3

x: 3
add-x

x: 3
y: 4

(x y)
(+ x y)

look up will be needed to get that value

Parameter, because
Scheme will regard that as a type error


key: chroma:document
string_value:
At this point, Scheme's default parameter passing mechanism is call-by-value

A local copy of the param. value is created

Student Question
What about
(define (sq x)
    (lambda (x) ((x x x) x x)))

Consider (sq 4)

(define sq
    (lambda (x)
        ( (lambda (x) ((x x x) x)) x)))


key: chroma:document
string_value:
G -> sq -> C(. X) -> ((Lambda (x) (x x x)) x) -> (x x x) / (x; 4) -> ((Lambda (x) (x x x)) x) -> (x x x) -> (evaluating lambda produces another closure)


key: chroma:document
string_value:
Function Composition

One imagines wanting a function cube - one idea might be (define (cube x) (x * x * x)) What if x^2 were wanted?

But another (better) would be to break the function down as (define (cube x) (x * (square x)))

Even without having first implemented the square function.

(Notice the divide & conquer thinking)

Another example:


key: chroma:document
string_value:
; functions may occur in other functions - for example
(define (sum-of-squares x y)
  (+ (square x) (square y)))

; we can use sum-of-squares as a building block in constructing
; further procedures

(define (f a)
  (sum-of-squares (+ a 1) (* a 2)))

(f 5)

These occurrences of a are said to be free in the body of f.

We can calculate (f 5) using either what is called the substitution model or the model introduced last time (the environment model).

See Ch 3 in AK 5

The substitution model: to evaluate a function call - (f 5) - substitute the value of the actual parameter for every free occurrence of the corresponding formal parameter in the function body

Review quantifiers (x, y, z) in your discrete math differentiate logic

For (f 5):
1) substitute 5 for a in (sum-of-squares (+ a 1) (* a 2))


key: chroma:document
string_value:
The substitution model should remind you of pre-calc algebra — and indeed, it suggests the possibility of an algebra of programs which would allow one to work with programs as easily as one would with algebraic equations. E.g.: do prgm1 and prgm2 compute the same values?

prgm1
↓
some sequences
of manipulations
↓
prgrm3
←-------------------
prgm2


key: chroma:document
string_value:
Can we at least start the environment model evaluation of (f 5)? Sure...

f
sum-of-squares
square
params (x y)
(x x) (+ square x) (square y)
square (x)
body (x)
(+ square x)
root (x)
(sum-of-squares)
(+ x 2)

After the functions have been defined in a (fresh) initial environment

We call (f 5)


key: chroma:document
string_value:
first step: using the closure for f
f
sum-of-squares
square
x
(x x)
(x x)
(+ (square x) (square y))
Create new frame
Second Step:
eval call to sum-of-squares
x: 6
y: 10
(+ (square 6) (square 10))
Third step
eval call to square
x: 10
(+ x x)


key: chroma:document
string_value:
Two useful observations on functions

; functions can also have 0 arguments
(define (one) 1)
; equivalently
(define one (lambda () 1))

; functions need not use all of their arguments
(define (two x y z) 2)
; equivalently
(define two (lambda (x y z) 2))


key: chroma:document
string_value:
; let's recall the earlier example

(define (f a)
  (sum-of-squares (+ a 1) (* a 2)))

; using applicative order, the evaluation
; of the call (f 5) proceeds as

; (f 5)

; ((sum-of-squares (+ a 1) (* a 2)) 5)

; (sum-of-squares (+ 5 1) (* 5 2))

; (sum-of-squares 6 10)

; (+ (square 6) (square 10))

; (+ (* 6 6) (* 10 10))

; (+ 36 100)

; 136

; using normal order, the computation
; proceeds differently:

; (f 5)

; ((sum-of-squares (+ a 1) (* a 2)) 5)

; (sum-of-squares (+ 5 1) (* 5 2))

; (+ (square (+ 5 1)) (square (* 5 2)))

; (+ (+ (* 5 1) (+ 5 1)) (* (* 5 2) (* 5 2)))

; (+ 6 6) (* 10 10)

; (+ 36 100)

; 136

In this case, applicative and normal order give the same result, but this is not always the case.


key: chroma:document
string_value:
applicative and normal order evaluation do not always give the same result. consider (define (p) (p) ) Clambda. observe that test, a user-defined function, will follow applicative order (define (test x y) (if (> x 0) 1 0) ) with this call, control never reaches the f... as scheme gets stuck trying to eva (p) what will happen if (test 0 (p)) is evaluated using applicative order? why? what will happen if it is evaluated using normal order? you can stop an infinite loop under DrRacket by using the redStop button at the top right of your screen. (if #t not (p) ) returns 0 -- because if is a special form, the infinite loop (p) is never evaluated to drive this point home, suppose that instead of if, we defined our own function, ne(if) (define (new-if predicate then-clause else-clause) (cond (predicate then-clause) (else else-clause))) (new-if (> 2 0) 3 4) (new-if (> 2 3) 3 4) ; this seems to 'work'! But should we try ... (let ((x 0)) (new-if (= x 0) (p) )) we find that we have an infinite loop, precisely because the standard evaluation rules apply to the user-defined function new-if. new-if is not a special form


key: chroma:document
string_value:
Boolean Functions and Conditionals

(define (myabs x)
  (cond ((> x 0) x)
        ((= x 0) 0)
        (else (- x))))

(myabs -4)

; another way

(define (myabs x)
  (cond ((<= x 0) (- x))
        (else x)))

; another way

(define (myabs x)
  (if (<= x 0)
      (- x)
      x))

;; >, =, < are primitive predicates
;; Scheme also supplies logical connectives such as and, or, not

(define x 6)

(and (> x 5) (<= x 10))

(not (> x 4))

#f

(not #f)

(= x 6)


key: chroma:document
string_value:
CSc 335 Sections M and R
Classes 3 and 4
February 1 and February 6 2024

; functions are values, too 
Scheme has first-class
functions
(lambda (x) (* x x))
lambda causes Scheme to create a
closure
You might enjoy
studying
the language
Lisp

That is, a function, along
with certain environmental
information

For example, in response to
define sq
lambda (x) (* x x))
The system augments its starting
environment as follows ->

namespace -> global


key: chroma:document
string_value:
starting environment - everything predefined in scheme is installed here

sq -> ?

t.
step 1 : install sq in G
(but we don't have a value
for sq yet, hence the
question mark)
step 2 : evaluate the lambda
closure created by lambda(x)(+ x x)

sq -> ?

The diagram shows closure with components labeled as 'environment part of closure' and 'code part of closure', as well as 'params (x)' and 'body (+ x x)'.


key: chroma:document
string_value:
Step 3: bind sq to the closure

The closure is a scheme object (much as the number 1 is a scheme object).

param -> (x)
body -> (* x x)

With this in place, what happens when we ask the system to evaluate (sq 4)? Not sq(4)!

function name

Scheme has dynamic typing

for the def-given, Scheme absolutely insists on exactly ONE argument.


key: chroma:document
string_value:
G is an environment of a sequence of frames. Global frame. When (sq 4) is requested the system opens a new frame. x: 4 or parent frame. The system sees that the frame associated with sq is G, so it makes G the containing frame of this new frame. 3. In the new frame, the formal parameter x is bound to the actual parameter (in this case, 4). We say 'x has value 4 in this frame'. 4. Next, scheme evaluates the body (x x x) in the new frame.


key: chroma:document
string_value:
frame

This new frame is memory allocated from the heap

x: A

(* x x)

What does it mean to evaluate (* x x) in the new frame?
i.e., in the env. formed by the new frame and S9, its parent.

We know from the scheme evaluation rule that the system needs to eval. x and x ... to evaluate * you can think of the


key: chroma:document
string_value:
system starting to look in the closest frame. But x is not defined in that frame (only x is), so the system goes to the parent frame and looks there for the definition of x. Since in this case the parent frame is G, the scope of x is found. So x is evaluated to the system nil. op, What about x? Again, the system starts in the closest


key: chroma:document
string_value:
frame in which x has been bound to A.

so now (* A A) can be computed, and to return,

5) still not done ... we need to free up the memory allocated to the new frame. This is done by the garbage collector (invented in the 1960s for LISP ) the idea is that storage which is no longer needed (ie, no longer "alive")


key: chroma:document
string_value:
How do we tell whether a storage is still alive? Storage is live if it is pointed at (even indirectly) from the initial environment.

Not alive, because no pointer from g
Alive because pointed at by sq


key: chroma:document
string_value:
When the entire evaluation is done, the situation is ________

No concerns about memory leakage, as GC takes care of the problem.

This evaluation mode is described in Ch. 3 of A&S – EXCEPT they have introduced assignment by them, they call it the environment model of evaluation.


key: chroma:document
string_value:
We can use this model to explain
Why (sq 5-) does not change
x if x has previously been
defined.
Suppose (define x 7)
(sq 5-)
and subseq. ask for x - you
will see that it is still 7. Why?


key: chroma:document
string_value:
Here's the situation after x and sq have been defined. This binding is shadowed by the local binding. (x x x) (x x x) x: 5 The system takes the first value of x that it finds as it works up the linked list of frames. Again, x is found in G and x is found in the new frame.


key: chroma:document
string_value:
After computing 25 the new frame is garbage - and we're left with
x = 17
and of course when now we ask for the value of x, we get 17.
(The 5 is gone for good)


key: chroma:document
string_value:
Abelson-Sussman format, which is under the hoo — the same as the lambda form. 
; some names are local, while others are global

(define x 3)

(define (add-x y) 
(+ x y))

x is free in the body of add-x

What happens when we have these definitions and then call for (add-x 6)?
Non own for (add-x 6)

x:3

add-x

;;Env pointer of the closure always goes to the frame in which the definition is created

(add-x y) 

;;system

(+ x y)

(+ x y)

6

3


key: chroma:document
string_value:
student question: What if add-x were defined

define (add-x x y)
(+ x y))

?  

Consider
(add-x 4 5)

x: 3

add-x ->

(Define (add-x x y) 
(+ x y) 

same as 
define 
add-x 
(+ x y)

x: 3 
x: 4 
y: 5 

(+ x y)

Note that you may not
call this new
version with just a single
(x + y) 
(+ x y) 

4 5


key: chroma:document
string_value:
What about
(define x 3)
(add-x x 4)

In this case, the system
would eval x to 3

x: 3
add-x

look up will
be needed
to get that
value

Parameter, because
Scheme will regard
that as a
type error


key: chroma:document
string_value:
At this point, Scheme's default parameter passing mechanism is call-by-value

A local copy of the param. value is created

Student Question

What about

(define (sq x)
  (lambda (x) ((x x) (x x))) x))

Consider (sq 4)

(define sq
  (lambda (x)
    ((lambda (x) ((x x) x)) x)
  ))


key: chroma:document
string_value:
G -> sq ((lambda x) (x x x)) x ((lambda x) (x x x)) x x; 4 ((lambda x) (x x x)) x evaluating the lambda produces another closure (x x x) (x x x)


key: chroma:document
string_value:
Function Composition

One imagines wanting a function cube - one idea might be
(define (cube x)
    (x * x * x))
What if sqrt were wanted?

But another (better) would be to break
the function down as

(define (cube x)
    (x * (square x)))

Even without having first implemented
the square function.

(Notice the divide & conquer thinking)

Another example :


key: chroma:document
string_value:
; functions may occur in other functions - for example
(define (sum-of-squares x y)
  (+ (square x) (square y)))

; we can use sum-of-squares as a building block in constructing
; further procedures

(define (f a)
  (sum-of-squares (+ a 1) (* a 2)))

(f 5)

We can calculate (f 5) using what is
called the substitution model or the model
introduced last time (The environment model)

The substitution model: to evaluate a function
call - s
(f 5) - substitute the value of
the actual parameter for every free ocurrence
of the corresponding formal parameter in the
function body

For (f 5):
1) substitute 5 for a in
(sum-of-squares (+ a 1) (* a 2))


key: chroma:document
string_value:
Eager evaluation (applicative order)

1) gives
   (sum-of-squares 6 10)
   if we substitute for the parameters before we sub for sum-of-squares

2) now do the same thing for sum-of-squares
   (+ (square 6) (square 10))

3) now do the same thing for square
   (+ * 36 100)

4) compute: 136

The substitution model is valid ONLY if assignment is absent - consider
   x = x+1

we substitute 6 for x and get
   (6 = 7)
   x

            (visual symbol for logical contradiction).


key: chroma:document
string_value:
The substitution model should remind you of pre-calc algebra — and indeed it suggests the possibility of an algebra of programs which would allow one to work with programs as easily as one would with algebraic equations. Eg: do prgm1 and prgm2 compute the same values? prgm1 -> some sequences -> of manipulations -> prgm3; prgm2 -> some sequences -> of manipulations -> prgm3


key: chroma:document
string_value:
Can we at least start the environment model evaluation of (f 5)? Sure...

init
env

f
sum-of-squares (sum-of-squares) (x) (sum-of-squares (x + x))
square
y
(square x) (square y)

params (x y)
body (+ square x) (square y)

After the functions have been defined in a (fresh) initial environment

We call (f 5)


key: chroma:document
string_value:
first step: using the closure for f
f
sum-of-squares
square
(sum-of-squares)
(+ a 1)
(+ a 2)
(a x y)
(+ square x) (square y)
create new frame
Second step:
eval call to sum-of-squares
(+ (square 6) (square 10))
x = 6
y = 10
a = 5
(sum-of-squares 6 10)
Third step:
eval call to
square
x=10
(+ x x)


key: chroma:document
string_value:
Two useful observations on functions

; functions can also have 0 arguments
(define (one) 1) ; one is a constant — every time it's called, it returns 1.
; equivalently
(define one (lambda () 1)) ; Works out with both eval models

; functions need not use all of their arguments
(define (two x y z) 2) ; body just returns 2, ignoring x, y, or z
; equivalently
(define two (lambda (x y z) 2))


key: chroma:document
string_value:
; let's recall the earlier example

(define (f a)
(sum-of-squares (+ a 1) (* a 2)))

; using applicative order, the evaluation
; of the call (f 5) proceeds as

; (f 5)

; ((sum-of-squares (+ a 1) (* a 2)) 5)

; (sum-of-squares (+ 5 1) (* 5 2))

; (sum-of-squares 6 10)

; (+ (square 6) (square 10))

; (+ (* 6 6) (* 10 10))

; (+ 36 100)

; 136

; using normal order, the computation
; proceeds differently:

; (f 5)

; ((sum-of-squares (+ a 1) (* a 2)) 5)

; (sum-of-squares (+ 5 1) (* 5 2))

; (+ (square (+ 5 1)) (square (* 5 2)))

; (+ (+ (* 5 1) (+ 5 1)) (* (* 5 2) (* 5 2)))

; (+ 36 100)

; 136

In this case, applicative and normal order give the same result, but this is not always the case.


key: chroma:document
string_value:
; applicative and normal order evaluation do not always give the same result
; consider
equivalently (define p (lambda () (p)))
observe that test, a user-defined function,
(define (p) (p))
will follow applicative order

(define (test x y)
  (if (= x 0)
      0
      y))

with this call, control never reaches the f...
as scheme gets stuck trying to evaluate (p)

; what will happen if (test 0 (p)) is evaluated using applicative order? why?
; what will happen if it is evaluated using normal order?

; you can stop an infinite loop under DrRacket by using the redStop button
; at the top right of your screen

; remark
(if #t 0 (p))
; returns 0 -- because if is a special form, the infinite loop (p) is never evaluated

; to drive this point home, suppose that instead of if, we defined our own function, new-if

(define (new-if predicate then-clause else-clause)
  (cond (predicate then-clause)
        (else else-clause)))

(new-if (> 2 0) 3 4)
(new-if (> 2 3) 3 4)

; this seems to work! But should we try...

(let ((x 0))
  (new-if (= x 0)
         0
         (p)))

; we find that we have an infinite loop, precisely because the standard evaluation
; rules apply to the user-defined function new-if. new-if is not a special form


key: chroma:document
string_value:
Boolean Functions and Conditionals
(define (myabs x)
  (cond ((> x 0) x)
        ((= x 0) 0)
        (else (- x))))
(myabs -4)
; another way
(define (myabs x)
  (cond ((<= x 0) (- x))
        (else x)))
; another way
(define (myabs x)
  (if (<= x 0)
      (- x)
      x))

; >, =, < are primitive predicates
; Scheme also supplies logical connectives such as and, or, not
(define x 6)
(and (> x 5) (<= x 10))
(not (> x 4))

#f
(not #f)
(= x 6)

; and has its own evaluation rule
; conjuncts are evaluated left to right, with immediate exit when one of them tests to false
; minus is two unary negation op
; necessary for formulating more general predicates
; not a scheme function, it is instead a macro
; this evaluation order is not part of any language standard


key: chroma:document
string_value:
general form: cond followed by a list of question-answer pairs. (? : "predicate" e1: "expression" optional else

note that cond and if do not obey the standard evaluation rule -

(cond (p1 e1) (p2 e2) ... (pn en) (else E)) is evaluated as follows: evaluate p1 -- if #t, then evaluate e1 and return this value; otherwise evaluate p2 -- if #t, evaluate e2 and return this value; otherwise evaluate p3 -- if #t, evaluate e3 and return this value

and so on. If none of the pi is #t, evaluate E and return this value

similarly, (if p1 e1 e2) evaluates e1, but not e2, if p1 is #t; it evaluates e2, but not e1, if p1 is #f

it is interesting to note that cond and if forms can be evaluated by themselves - they do not need to be embedded in a function

(define x 6)

(cond ((> x 6) 1)((= x 6) 2)(else 0))

cond and if can occur alone as input to scheme

(if (= x 6) 2 3)

and even such expressions as

(+ (cond ((> x 6) 0)(else 1)) 2)

and can also occur in ANY expression

A more interesting example:

(define (a-plus-abs-b a b) (if (> b 0) (+ a b) (- a b)))

make sure you fully understand the Scheme evaluation rule!


key: chroma:document
string_value:
We can return functions as values in Scheme.

(a-plus-abs-b 2 3) = (+ 2 3) = 5
(a-plus-abs-b 2 -3) = (+ 2 -3) = 5

It is instructive to note that and and or can be defined in terms of cond.

and in terms of cond?
cond ((x y)) = (and x y)

Let's look at the truth tables

x   y   (cond (x y))   (and x y)
T   F            F                 F
F   T            unspecified F
F   F            unspecified F

So the functions are NOT the same - though we could say that the restrictions of the functions defined by (cond (x y)) and (and x y) to all pairs whose first elt is T are the sam.


key: chroma:document
string_value:
Can we remove this restriction? Easy enough :
(cond ((< x y)
(else #f)))

You can check that this is possibly (and?)

Suggested HW: Show how OR can be implemented using cond.

Think about whether NOT could be implemented using cond.


key: chroma:document
string_value:
A&S Exercise 2.27, from HW 9

Deep Reverse Illustrated

(define (deep-reverse x)
  (cond ((null? x) '())
        ((not (pair? x)) x)
        (else (append (deep-reverse (cdr x))
                      (list (deep-reverse (car x)))))))

(deep-reverse of the cdr)

(deep-reverse of the car)

(recall/eg) (append '(1 2 3) 4) = 

or however fir presents this) which is not a list


key: chroma:document
string_value:
So you see that list (list 1 2 3) is forced by append and by the logic underlying, our tree diagrams we need to insert the link terminating at 1. We use append in the first place to preserve the root outdegree of the cdr. 

; the following is surely simpler to understand (and to prove ; correct) -- now looking at the argument as a list ; of subtrees

(define (alt-alt-deep-reverse tr)
   (cond ((null? tr) '())
         ((atom? tr) tr)
         (else (reverse (map alt-alt-deep-reverse tr)))))


key: chroma:document
string_value:
first: map the deep-rev function over t:
Giving a list of deeply reversed subtrees, but in the wrong order. How wrong? Well, it's exactly the reverse of what we want. And that accounts for the call to reverse, giving


key: chroma:document
string_value:
which is the list-of-subtree view of the derived result, We also discussed A&S exercises 2.31 (which needed 2.30) and 2.32. Again, these are part of HW 9. I ask you to work these out on your own, making note of where you get stuck, if in fact you do get stuck. Please bring your questions either to my office hours after the break, or to one of these zoom sessions (also after the break). I can go back to HW 7 if anyone has questions. Of course we need to discuss further problems in HW 8, the rest of HW 9, and the later HW assignments. This material will be emphasized heavily on both Quiz 3 and the final exam - please do not let it slide.


key: chroma:document
string_value:
CSc 335 Class 22 April 18 2024

; we start off our construction of a scheme interpreter by building its environment subsystem, followed by the interpreter presented in chapter 10 of the 'little schemer'

; environments implemented as tables, i.e., rib-cages

; pre:
; name is a symbol
; table is a rib-cage, that is, a list of entries, where each entry is a list (names values), where names is a list of symbols and names is the corresponding list of values. The lists names and values have the same length, and the kth name corresponds to the kth value.
; table-f is a function of one argument, name, which specifies the action taken when name does not exist in any of the entries of the table

; post:
; returns the value associated with name in table - note that the search is entry by entry, starting with the topmost entry.
; entry - if this exists, otherwise returns (table-f name)

(define lookup-in-table
(lambda (name table-f)
(cond
((null? table) (table-f name)) catastrophic fail
(else (lookup-in-entry name
(car table)
(lambda (name)
(lookup-in-table name
(cdr table)
 table-f))))))

You might try working out a proof for the suite of functions implementing the environment subsystem. In particular, work out the specification of this subsystem, and @see related documentation.


key: chroma:document
string_value:
; pre: name is a symbol
; entry is a pair of lists (names vals), where names is a lat, and vals is a list of values
; with length at least as long as that of names
; entry-f is a function which specifies the action taken when name does not occur in names
;
; post: returns the value associated with name, with positional association: the kth name is assigned to the kth value
; if name occurs in the names component of entry. Otherwise calls entry-f with argument name

(define lookup-in-entry
  (lambda (name entry entry-f)
    (lookup-in-entry-help name
                          (names entry)
                          (vals entry)
                          entry-f)))

(define lookup-in-entry-help
  (lambda (name names vals entry-f)
    (cond
      ((null? names) (entry-f name))
      ((eq? (car names) name) (car vals))
      (else (lookup-in-entry-help name
                                  (cdr names)
                                  (cdr vals)
                                  entry-f)))))

; simultaneously cdr down the lists names and vals


key: chroma:document
string_value:
; here are constructor and selectors for entries

(define build
  (lambda (s1 s2)
    (list s1 s2)))

(define new-entry build)

(define names
  (lambda (entry) (car entry)))

(define vals
  (lambda (entry) (cadr entry)))

; here are the basic definitions for tables

(define initial-table '())

(define table-f
  (lambda (name)
    (car (quote ()))))

; note that as (car (quote ())) throws an error: when the table is empty,
; attempting to look anything up in it is an error. See the definition
; for lookup-in-table, above.

; we could just use the error primitive, but I wanted to explain
; the presentation given in tls. Why didn't they use error? Perhaps
; to make the point that it is not necessary to have this primitive.
; Or perhaps because error was not included in standard scheme at
; the time the book was written.

(define extend-table cons)


key: chroma:document
string_value:
; testing data

(define names1 '(a b))
(define vals1 (list 1 2))
(define entry1 (new-entry names1 vals1))

(define names2 '(a c))
(define vals2 (list 3 4))
(define entry2 (new-entry names2 vals2))

(define table1 (extend-table entry1 initial-table))
(define table2 (extend-table entry2 table1))

; a few tests

(lookup-in-table 'a table2 table-f)

; observe that this returns 3, not 2: a is found in the topmost rib, and so the second rib is never searched.

(lookup-in-table 'b table2 table-f)

; b is not present in the first rib, and so the second rib is searched.

; from the call
;; (lookup-in-entry name
;;   (car table)
;;   (lambda (name)
;;     (lookup-in-table name
;;       (cdr table) table-f)))
; we see that entry-f has been bound to (lambda (name) (lookup-in-table name (cdr table) table-f))

; and that the topmost entry is discarded for the continuation of the search. We are cdring down t... (image cut off)

(lookup-in-table 'd table2 table-f)

; as expected, this fails


key: chroma:document
string_value:
; Perhaps one additional remark is in order for this first look at the environment subsystem of the tls scheme interpreter: observe that ANY scheme value at all can occur in the values lists. We used integer values for our example, but soon we will take advantage of the genericity of scheme's lists by (for example) having function ; descriptions occur in them

(define names3 (list 'e 'id))
(define vals3 (list 17 '(lambda (x) x))) ; (lambda (x) x) is here regarded as a description of the identity function
(define entry3 (new-entry names3 vals3))
(define table3 (extend-table entry3 table2)) .

This point is important for understanding
how TLS implements higher-order
functions.


key: chroma:document
string_value:
Now we begin discussion of the entire scheme interpreter presented in Chapter 10 of The Little Schemer. This interpreter, which we shall refer to as TLS Scheme, or just TLS, implements a subset of Scheme, which we shall also refer to as TLS. Whether we mean the interpreter or the language will be clear from context.

TLS scheme has just 6 types of expressions, named *const, *quote, *identifier, *lambda, *cond, and *application.

More precisely, the syntax of TLS can be given inductively as follows:

basis step of an inductive definition of TLS
:
numbers, #t, #f, cons, car, cdr, null?, eq?, atom?, zero?, add1, sub1, number? have type *const -- we may find it desirable to add others later -- as you will see, this is very easy to do

symbols have type *identifier

inductive step of an inductive definition of TLS
:
(quote x) has type *quote, where x is any TLS expression

(lambda varlist lambda-body) has type *lambda, where varlist is a list of *identifiers and lambda-body is any TLS expression

(cond (p1 e1) ... (pk ek)) has type *cond, where the pi and ei are any TLS expressions

(e1 e2 ... en) has type *application, where the ei are any TLS expression

Of course, syntax says nothing at all about the meaning of expressions - it is the interpreter which supplies meaning for expressions in TLS.


key: chroma:document
string_value:
; It will be helpful to look at TLS (The interpreter) in layers. Let us begin by examining the evaluation of expressions without variables. You may find the complete interpreter posted on Piazza.
;if the top level of the interpreter is given as:
(define value
(lambda (e)
(meaning (quote ())))

(define meaning
(lambda (e table)
((expression-to-action e) table)))

;then we can say that the interpreter computes the value of a given TLS expression e by calling the meaning function with the empty list as table parameter. Here the system will use tables - as sketched above - to organize information about variables/values. Our limitation to variable-free expressions for this initial look allows us to assume that table always has the value 'quote(())'.

;from the definition of the meaning function, we are motivated to look into expression-to-action

(define expression-to-action
(lambda (e)
((atom? e) (atom-to-action e))
(else (list-to-action e)))))

; where

(define atom?
(and (not (null e)) (not (pair e))))

;now one thing at a time, in an attempt to manage complexity - we look at atom-to-action

(define atom-to-action
(lambda (e)
(cond
((number? e) '*const)
((eq? e '#f) '*const)
((eq? e '#t) '*const)
((eq? e (quote cons)) '*const)
((eq? e (quote car)) '*const)
((eq? e (quote cdr)) '*const)
((eq? e (quote null?)) '*const)
((eq? e (quote eq?)) '*const)
((eq? e (quote atom?)) '*const)
((eq? e (quote zero?)) '*const)
((eq? e (quote add1)) '*const)
((eq? e (quote sub1)) '*const)
((eq? e (quote number?)) '*const)
(else 'identifier))))

; where


key: chroma:document
string_value:
(define *const
  (lambda (e table)
    (cond
      ((number? e) e)
      ((eq? e #t) #t)
      ((eq? e #f) #f)
      (else (build (quote primitive) e))))

; with

(define build
  (lambda (s1 s2)
    (list s1 s2)))

; and

(define *identifier
  (lambda (e table)
    (lookup-in-table e table initial-table)))

; as we are assuming there are no identifiers, for the moment, we say nothing further at this time regarding
; *identifier.

{*const 1 '()} = 1
(primitive sub)


key: chroma:document
string_value:
As we noted last time, we can already evaluate some simple expressions with the code we have:
(value 't')
; returns 1
(value '+t')
; returns #t
(value 'sub1')
; returns ('primitive sub1')
; here the tag 'primitive' will be used later to further steer the evaluation of expressions involving sub1.

Why quoted? Well, without the quote, Rocket Scheme will of course eval. but why to value. This is on for numbers and booleans, but what about sub1 is not defined in R5RS, so this would trigger an error - even though sub1 is defined in TLS.
The main idea: we quote the input to value so that OUR interpreter gets to evaluate the input.


key: chroma:document
string_value:
(define list-to-action
(lambda (e)
  (cond
    ((not (pair? e)) (error "bad syntax" e))
    ((eq? (car e) 'quote) (list 'quote (cadr e)))
    ((eq? (car e) 'lambda) (list 'procedure (cadr e) (caddr e)))
    ((eq? (car e) 'if)
      (if (eval (cadr e) env)
          (eval (caddr e) env)
          (eval (cadddr e) env)))
    (else (list 'application (map eval (cdr e) env)))))))

; with

(define *application
  (lambda (e table)
    (myapply
      (meaning (function-of e) table)
      (evals (arguments-of e) table))))

(define function-of car)

(define arguments-of cdr)

; and

(define myapply
  (lambda (fun vals)
    (cond
      ((primitive? fun) (myapply-primitive (second fun) vals))
      ((non-primitive? fun) (myapply-closure (second fun) vals))))))

; with

(define primitive?
  (lambda (l)
    (eq? (first l) (quote primitive))))

; and

(define first car)

(define second cadr)

(define third caddr)

; and

(define myapply-primitive
  (lambda (name vals)
    (cond
      ((eq? name (quote cons)) (cons (first vals) (second vals)))
      ((eq? name (quote car)) (car (first vals))))))


key: chroma:document
string_value:
(define myapply-primitive
(lambda (name vals)
  (cond
    ((eq? name (quote cons))
      (cons (first vals) (second vals)))
    ((eq? name (quote car))
      (car (first vals)))
    ((eq? name (quote cdr))
      (cdr (first vals)))
    ((eq? name (quote null?))
      (null? (first vals)))
    ((eq? name (quote eq?))
      (eq? (first vals) (second vals)))
    ((eq? name (quote atom?))
      (atom? (first vals)))
    ((eq? name (quote zero?))
      (zero? (first vals)))
    ((eq? name (quote add1))
      ((lambda (x) (+ x 1)) (first vals)))
    ((eq? name (quote mul))
      (* (first vals) (second vals)))
    ((eq? name (quote sub1))
      (sub1 (first vals)))
    ((eq? name (quote number?))
      (number? (first vals)))))

; and finally

(define evalis
(lambda (args table)
  (cond
    ((null? args) (quote ()))
    (else
      (cons (meaning (car args) table)
           (evalis (cdr args) table))))))

; now we can evaluate (sub1 4)
; on board, in class
(value 'sub1 4) gives just 3.

Sub1 note: note that sub1 is an R5RS function defined by the user. Use an augmented interpreter code
(define (sub1 x)
  (- x 1))


key: chroma:document
string_value:
CSC 335 Class 23 April 30 2024
We start by looking at the evaluation of more complex expressions, to better understand the recursive structure of the interpreter.

(define list-to-action
  (lambda (e)
    (cond
      ((atom? (car e))
        (cond
          ((eq? (car e) (quote quote))
            'quote)
          ((eq? (car e) (quote lambda))
            'lambda)
          ((eq? (car e) (quote cond))
            'cond)
        (else *application*))))

(define primitive?
  (lambda (l)
    (eq? (first l) (quote primitive))))

(define first car)

(define second cadr)

(define third caddr)

;;; with

(define *application
  (lambda (e table)
    (myapply
      (meaning (function-of e) table)
      (evlis (arguments-of e) table))
    (define-function-of car)
    (define arguments-of cdr)))

(define myapply
  (lambda (fun vals)
    (cond
      ((primitive? fun)
        (myapply-primitive
          (second fun) vals))
      ((non-primitive? fun)
        (myapply-closure
          (second fun) vals)))))

;;; and

(define myapply-primitive
  (lambda (name vals)
    (cond
      ((eq? name (quote cons))
        (cons (first vals) (second vals)))
      ((eq? name (quote cdr))
        (cdr (first vals)))
      ((eq? name (quote null?))
        (null? (first vals)))
      ((eq? name (quote eq?))
        (eq? (first vals) (second vals)))
      ((eq? name (quote atom?))
        (atom? (first vals)))
      ((eq? name (quote zero?))
        (zero? (first vals)))
      ((eq? name (quote add1))
        ((lambda (x) (+ x 1)) (first vals)))
      ((eq? name (quote mul))
        ((* first vals) (second vals)))
      ((eq? name (quote sub1))
        ((sub1 (first vals))))
      ((eq? name (quote number?))
        (number? (first vals)))))

Now we can evaluate:
(value ('mul (add1 3) (sub1 4)))
meaning ('mul (add1 3) (sub1 4))
-> ((expression-to-action 'e) (list-to-action 'e) (e '() '))


key: chroma:document
string_value:
(applyfun e '())
(apply    (meaning '()) (evlis ((add 3) (sub 4)) '()))
maps meaning with the '()' table over this
('primitive mul') (( 4   3 ))
(myapply->primitive ( 4 3 )) -> 12


key: chroma:document
string_value:
(define list-to-action
(lambda (e)
  (cond
    ((atom? (car e))
    (cond
      ((eq? (car e) (quote quote)) '*quote)
      ((eq? (car e) (quote lambda)) '*lambda)
      ((eq? (car e) (quote cond)) '*cond)
      (else '*application)))
    (else '*application)))

; with

(define *application
  (lambda (e table)
    (myapply
      (meaning (function-of e) table)
      (evlis (arguments-of e) table))))

(define function-of car)

(define arguments-of cdr)

; and

(define myapply
  (lambda (fun vals)
    (cond
      ((primitive? fun) (myapply-primitive (second fun) vals))
      ((non-primitive? fun) (myapply-closure (second fun) vals)))))

; now we can evaluate:

(value '(cond (#f 1) (#t 2)))

(value '(cond ((number? (quote x)) 1) (else 2)))

(value '(mul (add1 (cond (#f 1) (#f 2) (else 3))) 4))


key: chroma:document
string_value:
finally, we add the lambda subsystem -- in TLS, the only way we associate values with variables is via lambda. (define list-to-action (lambda (e) (cond ((atom? (car e)) ((cond ((eq? (car e) (quote quote)) (*quote) ((eq? (car e) (quote lambda)) (*lambda) ((eq? (car e) (quote cond)) (*cond) (else *application)))))) ; with (define *application (lambda (e table) (myapply (meaning (function-of e) table) (evalis (arguments-of e) table)))) (define function-of car) (define arguments-of cdr) ; and (define myapply (lambda (fun vals) (cond ((primitive? fun) (myapply-primitive (second fun) vals)) ((non-primitive? fun) (myapply-closure (second fun) vals))))) (value '(lambda (x) (add1 x) 3)) builds a closure so you can see what closures actually look like (define *lambda (lambda (e table) (build (quote non-primitive) (cons table (cdr e))))) (define table-of first) (define formals-of-second) (define body-of-third) (define non-primitive? (lambda (l) (eq? (first l) (quote non-primitive)))) (define myapply-closure (lambda (closure vals) (meaning (body-of-closure (extend-table (new-entry (formals-of closure) vals) (table-of closure)))))) with this last definition, we pause to make sure everyone understands the concept of closure we now actually have a Turing-powerful programming system we will start by looking at lambda without recursion, and then see how one can use a device called the Y-combinator to implement arbitrary recursive functions.


key: chroma:document
string_value:
This is the closure created by this particular lambda: 

 general form of a closure in LISP 

 (stable formals body) 

 What does myapply do with this? 

 (myapply-closure '(T . (x add1 x))  (3)) 

 Meaning (add1 x) ((x (3))) 

 ie - eval(add1 x)^(x) - (3)


key: chroma:document
string_value:
(define list-to-action
(lambda (e)
  (cond
    ((atom? (car e))
      (cond
        ((eq? (car e) (quote quote))
          *quote)
        ((eq? (car e) (quote lambda))
          *lambda)
        ((eq? (car e) (quote cond))
          *cond)
        (else *application))))
; with
(define *application
  (lambda (e table)
    (myapply
      (meaning (function-of e) table)
      (evals (arguments-of e) table))))
(define function-of car)
(define arguments-of cdr)
; and
(define myapply
  (lambda (fun vals)
    (cond
      ((primitive? fun)
        (myapply-primitive
          (second fun) vals))
      ((non-primitive? fun)
        (myapply-closure
          (second fun) vals)))))
(define *lambda
  (lambda (e table)
    (build (quote non-primitive)
      (cons table (cdr e)))))
(define table-of first)
(define formals-of second)
(define body-of third)
(define non-primitive?
  (lambda (l)
    (eq? (first l) (quote non-primitive))))
(define myapply-closure
  (lambda (closure vals)
    (meaning (body-of closure)
      (extend-table
        (new-entry
          (formals-of closure)
          vals)
        (table-of closure))))
(value '((lambda (y)
  (lambda (x) (cons x y)))
  3
  4))
(value '((lambda (x) (add1 x))
  ((lambda (x) (add1 x)) 4)))
; emerging (lambda x)(add1 x)) (5)
; via list-to-action to *application, then to myapply
; ?
; This punctuation can even occur?
; No


key: chroma:document
string_value:
(cmyapply meaning (lambda (x) (add1 x)) '(3))
meaning => (evals ((lambda (x) (add1 x)) 3) => 4))

(cmyapply-closure (non-primitive (G (x) (add1 x)) 1))
(5)

(meaning (add1 x) ((x 5)))

(value '((lambda (y)
          (lambda (x) (cons x y))))
  3)
  4)

conjecture
we expect at some point to see a
table with 2 cols:

  (x)     (3)
  (y)     (4)

Work this out first by hand,
as we've been doing -- and
then check your computation by 'instrumenting'
the #!scm code with display and newline


key: chroma:document
string_value:
(define list-to-action
(lambda (e)
(cond
((atom? (car e)) quote)
(cond
((eq? (car e) (quote quote)) *quote)
((eq? (car e) (quote lambda)) *lambda)
((eq? (car e) (quote cond)) *cond)
(else *application))))

; with

(define *application
(lambda (e table)
(myapply
(meaning (function-of e) table)
(evlis (arguments-of-e table)))))

(define function-of car)
(define arguments-of cdr)

; and

(define myapply
(lambda (fun vals)
(cond
((primitive? fun) (myapply-primitive (second fun) vals))
((non-primitive? fun) (myapply-closure (second fun) vals)))))

; also defines lambda handling

(define *lambda
(lambda (e table)
(build 'quote-non-primitive
(cons table (cdr e)))))

(define table-of-first)
(define formals-of-second)
(define body-of-third)
(define non-primitive?
(lambda (l)
(eq? (first l) (quote non-primitive))))

(define myapply-closure
(lambda (closure vals)
(meaning (body-of-closure) (extend-table (new-entry (formals-of-closure) vals) (table-of-closure))))

; example of evaluation:
(value '((lambda (x z)
(cons x
((lambda (x y) (cons z x)) 3 4)
) 1 2)))

Handwritten notes:
'Will pick up here tomorrow'
'Take what this x is evaluated to'
'x z → (x z)'


key: chroma:document
string_value:
need an interesting conjecture on the tables which appear as this computation proceeds. Perhaps (x y) + (13 47)? Table for (x+z) = '1 2') inner lambda. Will we see the table both grow and shrink? Hmmm... looks like growth, but no shrinkage. Leads to your asking: how could the program be modified so that we see both growth and shrinkage? (value '(lambda (x z) (cons x (lambda (x y) (cons z x))) 3 4)) - maybe a and a subsequent reference to x?? Consider the inner lambda. (value '((lambda (x z) cons (lambda (x y) cons z x)) 3 4 1 2))


key: chroma:document
string_value:
; next, let's translate the following simple illustration of a closure to tls-scheme

(let ((x 3))
  (let ((x 4)
        (f (lambda (y) (+ y x))))
    (f 2)))

; to get

(value '((lambda (x)
           ((lambda (x f) (f 2))
            4
            (lambda (y) (+ y x))))
         3))

; you see that tls-scheme features lexical scope and first-class functions --
; after you have had some time to think about how one might go about
; certifying that TLS correctly implements
; lexical scope and first class functions, I will describe proof outlines in class,
; and then ask you to write
; up the arguments for homework. Big surprise: induction is involved.


key: chroma:document
string_value:
Class 9 CSC 335 February 27 and 29 2024

Tree Recursion

The first example is one that CS-1 text book writers love to use to discredit the whole idea of recursion. We compute the nth Fibonacci number, where the Fibonacci numbers are

0 1 1 2 3 5 8 13 21

usually with 0-based indexing

0 1 2 3 4 5 6 7 8

Our program is to input an integer n >= 0 and return the nth Fibonacci #

define (fib n)
  cond ((zero? n) 0))
       ((one? n) 1)
       (else (+ fib (- n 1)) (fib (- n 2)))
       )

This is just the scheme version of the definition (so not focussing here on hw development)
but it nonetheless brings up some interesting points.


key: chroma:document
string_value:
First: how do we deal with a 2-case basis step? Basis step: any computation done without a recursive call.

Second question: how do we deal with two recursive calls?

Let's do the second: as before, we may assume that the recursive calls work correctly PROVIDED the precondition is satisfied when the calls are made

-> need n-1 >= 0 is an integer when (Fib(n-1)) is called
-> need n-2 >= 0 is an integer when (Fib(n-2)) is called

So the program computes the nth Fib # for n >= 2, as the sum of the (n-1)st and (n-2)nd numbers.


key: chroma:document
string_value:
and (n-2)nd fib # - which is correct.
For the basis step, one must show the correctness of each case separately.

This seems easy enough - so why the abuse heaped on this poor program?
The reason is that it is extremely inefficient - bad recursions are indeed to be avoided (but this does not mean that recursion is to be avoided).


key: chroma:document
string_value:
The inefficiency can be seen in a pattern of calls expansion. E.g.: 

fib5        fib5
    \        /
    fib4  fib3
      \   /     \   /
     fib3 fib2 fib2 fib1
   /    \    /    \ 
 fib2 fib1 fib1 fib0

The problem is that so many computations are duplicated. This program requires exponential time -> idea for proof is to count the number of times fib0 and fib1 are computed.


key: chroma:document
string_value:
Could use memoization to store previously computed results to improve efficiency - but of course this requires assignment. 

Let's next develop an iterative Fibonacci program.

(At the board)


key: chroma:document
string_value:
(define (fib n)

(define (fib-iter curr prev count)
  (cond ((= count n) curr)
        (else (fib-iter (+ curr prev) curr (+ count 1)))))

(cond ((= n 0) 0)
      ((= n 1) 1)
      (else (fib-iter 1 0 1))))

; what are the design roles of curr and prev?
; what is the invariant? is this version correct?


key: chroma:document
string_value:
Question: are termination arguments always so easy? Collatz conjecture

No - go ahead and search for the '3n+1 problem' for an example of an apparently simple while loop with completely unknown termination properties.

But even here - for the recursive fib program - while it is clear that the fib (n-1) + fib (n-2) calls will stop - can we be equally sure that new (fib (- n 2)) calls will stop?

Maybe we just blow through the stopping cases!

See if you can work this out.


key: chroma:document
string_value:
Class 10 and HW5 CS 335 Sections M & R

Developments — which you should attempt before
hand — to be sketched in class

1. Recursive and iterative procedures to decide
whether an integer n ≥ 0 is sorted
(i.e whether its digits occur in non-decreasing
order)

2. Recursive and iterative procedures to return
the number formed from an integer
n ≥ 0 by sorting the digits of n.
→ insertion sort
→ selection sort

3. Recursive and iterative procedures
which input integers m ≥ 0 and n ≥ 0
and which decide whether all the
digits of m occur in n, in the same order.

4. Recursive and iterative procedures which
input an integer n ≥ 0 and a
digit d and which return the
number obtained from n by removing the
leftmost occurrence of d.


key: chroma:document
string_value:
Classes 1 and 2 January 25 and 30, 2024

We begin by describing the purely functional sublanguage of R5RS-Scheme

Purely functional means no assignment and, more generally, no destructive programming; after initial definition, values are not changed or destroyed.
We focus on what is called declarative programming, in contrast to the imperative programming style.

At the basis of this language are a few primitive data types. Finally, we'll make use of just three of these:
- numbers
- booleans
- functions

Numbers can be integers, rationals, or reals (complex, too, but we won't use those)
integers: 'infinite' precision
rationals: 1/3 displays as 1/3, not as 0.333333...
reals: subject to same precision limitations as in C


key: chroma:document
string_value:
Booleans are just #t (true) and #f (false) not themselves defined. This is one difference between R5RS and the version of MIT Scheme used in Abelson & Sussman (SICP). They use nil for #f as well as for the empty list. Note that nil is not even defined in R5RS. Functions come in two main flavors – primitives and user-defined. For this introductory look at the basis of R5RS, we’ll focus on the arithmetic primitives. Examples of these are +, -, *, /, modulo, quotient, remainder, etc. I urge you to get in the habit of taking incremental reads of the R5RS manual where you will find a complete list of new primitives. Numbers, Booleans and primitive functions can be said to be self-evaluating: type one in, and get it back unchanged.


key: chroma:document
string_value:
We can (of course) use these primitives to form more complex expressions. Before showing some of these, let me emphasize the critical role of parentheses in Scheme. Unlike their usage in math papers, where they are NOT just for the convenience of the reader. When we write
(+ 2 3)
For example, the parens are regarded as an instruction:
• evaluate the leftmost entry - throw an error if its value is not a function
• evaluate the remaining entries
• apply the value of the leftmost entry to those values
• return the result


key: chroma:document
string_value:
For (+ 2 3) + evals to the built-in + function, 2 evals to the internal representation of 2, and 3 to the internal rep. of 3 - The application then returns 5, 

Similarly for (* 5 2) returns 10 and (< 10 2) returns False and so on. 

Interestingly, these primitives are generic, in at least two ways. 

First (unlike C ), + is happy to accept reals and rationals as well as integers; indeed, even mixtures of them 

(* will record that these lectures moved over to racket for some examples.)


key: chroma:document
string_value:
Main takeaways from first excursion: generic and regards numeric types (i.e., we do not need separate functions for int, real, rat, etc.) and allows mixed types. Despite the absence of user-created type tags, types dynamically detected at runtime are nonetheless present – one cannot compute (+ #f #f), for example – the primitive + requires that its arguments be numbers. A second kind of genericity has to do with the number of arguments. Main takeaways from second excursion: Generic also as regards the number of arguments – e.g., (+ 1 2 3) computes 6. The idea is that + regards its args as a list, and it returns the sum of the numbers in that list. On this mode, (+) ought also


key: chroma:document
string_value:
make sense and indeed (+) returns 0 --- the sum of all the numbers in the empty list. Similarly for the other arithmetic primitives? Explore! Note that (*) returns 1, not 0, in line with mathematical convention. Even more complex expressions can be formed if we nest parens. For example, (+ (* 3 4) 2) is eval'd using the same rule (the Scheme Evaluation Rule) . + evals to the builtin + function (call it plus) . 2 evals to two


key: chroma:document
string_value:
The rule is applied recursively to get the value 12 for (* 3 4). So (+ (* 3 4) 2) = 14. There is no limit on how nesting depth, but as these expressions become larger, it will be helpful to have a simple visualization for them. We use: traveled in the expected way: * / \ 3   4 + 2


key: chroma:document
string_value:
It is important to know that one should not expect that t evaluates its args in left to right order, In fact, we know nothing at all about the eval. order except that the arguments to a function (as well as the function itself) must have been evaluated before the function can be applied. In fact, this is the ONLY notion of order which matters for this kind of programming. As you will soon see, this explains why the semicolon (;) has in Scheme.


key: chroma:document
string_value:
For any functional language, noting the importance of this in C. Essentially, 'before and after' are concerns in Scheme only as they apply to the evaluation of a function and its args before the function can be applied to these arguments. In other words, P; Q (i.e., 'do P, then Q') makes NO sense in pure functional Scheme. We will see how function composition makes up for this; assuming...


key: chroma:document
string_value:
We'll get at variables via the special form define.

For example

(define x 3)

We say that define is a special form because the 'scheme Evaluation Rule' discussed last time does not apply for define.

So - how does define work?

Initially, scheme opens a global namespace in which the primitives (etc.) are defined, but no user variables are defined.

(initial (empty) environment)

-> (define x 3) ->

x : 3

We write x : 3 to indicate that x is a name of the value 3.


key: chroma:document
string_value:
We say 'x is bound to 3'. Another suggestive picture is

x -> 3

If we were now to

define y 3

then the global env would be

| x: 3 |
| y: 3 |

with x -> 3 as the corresponding

pointer diagram.

Once (define x 3) has been evaluated, then, within the scope

of this defun, you may replace all occurrences of x by 3 without

changing the meaning of the substituted expression.


key: chroma:document
string_value:
(define x 3) 

(+ x 4) 

means (+ 3 4) : anywhere x occurs, we can replace it with a 3. This illustrates referential transparency. 

So what? Well, consider the contrast with C. After the declaration 

int x = 3; 

consider 

x = x + 1; 

Can we replace x by 3 without wrecking the meaning? Clearly not. 

What's responsible for the difference?


key: chroma:document
string_value:
In C, x is NOT a name for 3 after this initialization—rather it is a name for a box (memory location) containing 3.
x -> [3]                                   x -> 3


in C                                       in Scheme

(*) In scheme, as in C, scopes frequently have "holes" typically brought about by local variables

local scope                                       global scope
    local scope


key: chroma:document
string_value:
One way of changing x and its scope:

(define x 3)

scope of x
in head, x names 3

(define x 4)

(new) scope of x
in HEAD, x names 4

Hey! Why isn't this just assignment?
Well, it is: it is definitional assignment, making an association between x and its value.
But it is extremely restricted, and Scheme in no way allows anything


key: chroma:document
string_value:
equivalent to x = x + 1. In particular, define cannot occur within an expression nor in the body of a program.

What haven't we explained yet about define?

What about (define x (+ x 1))?

This would assume that x has already been defined. The eval rule for (define x e) is something like

(1) install x in the global env, perhaps without a value.


key: chroma:document
string_value:
x: ?

2) Evaluate e to get its value, say v

3) Finish the binding - so that when the eval. of the def is complete, we would have x: v

Take a moment to think why the standard evaluation rule would not work for 'define'.


key: chroma:document
string_value:
(define x e)
The standard rule would say
standard rule
   eval: The primitive define
   eval x (but x is usually
   here being defined
   for the first time) -->
   so x has no value!
   eval e
Once a variable has been bound to
a value, we can use it anywhere
we like as (eg) a shorthand for
that value. often
(define pi 3.1415 ...)
we could write (+ 2 pi)
(+ 2 (* 3 pi))
.
.
.


key: chroma:document
string_value:
We talked about the special form define - but although I mentioned the severe restrictions on the use of define, I still need to give you an example of forbidden usage of define in an expression. Here's one (+ (define x 3 ) 2) You can use the Check Syntax button in Dracket to see the explanation of why this won't work. Recall the C distinction between expressions and statements. Pure functional scheme does not have statements. So this restriction on define is very strong. Should also have mentioned that 'define' may not be used as a variable – it is a reserved word (***)


key: chroma:document
string_value:
Just about any combination of numbers and characters can be used to form identifiers in Scheme. There are some restrictions
- no embedded white space
- no embedded parens
- perhaps others, but I am not thinking of them just now
By default, Scheme does not differentiate upper and lower case letters.
You will find the complete set of rules in the R5RS reference manual, which I have posted on Teams.


key: chroma:document
string_value:
Another special form is lambda. When we enter (lambda (x) (* x x)) in R5RS, the system returns #procedure. We conclude that the lambda form is a 'stand-alone' Scheme value. In fact, such forms are Scheme values in the same sense that, e.g., numbers and booleans are Scheme values. We will see that, just as numbers and booleans can be returned by function calls, so too can functions be returned by function calls. In fact, functions can occur as components of data structures - e.g., we will see lists of functions. One says that, in Scheme, functions are first class.


key: chroma:document
string_value:
It's useful to take the lambda form apart:

lambda (x) (* x x)

lambda is a reserved word

formal parameter list

lambda body (G**)

lambda (x y) (* x y) local

The formal parameters each open a scope, which is precisely the body of the lambda form.

Returning to the lecture discussion, these scopes can be said to be holes in any containing scope of the same variable.

For example:


key: chroma:document
string_value:
define x 1
This x is 1
(lambda (x) (+ x 2))
This x is 4
(define x 6)
This new x is 6
You will want to experiment...consider
define x 2
(lambda (x) (+ x 2))
(lambda (x) (+ x 3))
What is the value of this x?
The value is 1
Value returned by this application
(+ 4 2) = 6


key: chroma:document
string_value:
(Anything of the form
(fn arg1 arg2 ... argk)
is said to be an application.)

Note that define and lambda play nicely together:

(define square
    (lambda (x) (* x x)))

Abelson & Sussman notation for this function definition is

(define (square x)
    (* x x))

They prefer to not write the lambda.

(This is the first instance we've seen of what is called syntactic sugar)


key: chroma:document
string_value:
Having defined square this way, one uses it as follows

(square 2)

By the scheme eval-rule, square would be evaluated to

(lambda (x) (* x x))

and then the evaluation continues as we have described, via the substitution rule - substitute the value of the actual parameter, 2, for the formal parameter, x, everywhere x occurs in the lambda body.


key: chroma:document
string_value:
Still to discuss in connection with functions:
- type checking
- what is a function value?
- can we have an operational description of function evaluation?
- what happens if there is an undefined variable in a lambda body?
- how can functions be composed?
- your questions

Mentioned in class while using DrRacket:
- newline
- display

These primitives can be useful in understanding Scheme - allowing simple instrumentation of our programs (eventually and now) of our interactions with the language.

I will summarize my use of them momentarily. First however, it is important to know that neither of the calls

(newline)
(display e)

(where I assume e has been given a value previously) return a value which can be input to another function.


key: chroma:document
string_value:
If one attempts (+ (display 1) 2) for example an error will be thrown - complaining about the fact that (display 1) returns no useable value. All it does is print 1 to the screen (that's the side-effect).

That being said, here is an annotated copy of an interaction with racket

(define x 1) ;entered into a fresh window
(newline)
(display x) ;see what happens when
(newline) ;you remove the newlines.

x is 2 in the local scope

(lambda (x y) (+ (+ x y 2)) x) 2 4 opens a local scope for x and y

(newline)
(display x) ; local scope has disappeared
(newline) ; and x is again 1.

(define x 17)
(newline) ; now x is 17 in the global
(display x) ; scope: there is no record
(newline) ; of it ever having been 1

Similarly, there is no record of x ever having been 2 ; you cannot reach back into a scope


key: chroma:document
string_value:
What if, at the end of this session, we were to ask for a display of the value of 'y'? An error message, complaining about 'y' being undefined, would be shown.

What about writing just 'y', instead of '(display y)'? Same error; 'y' has first to have some form display can evaluate its argument to.

Note that display takes only a single argument: R5RS does not have the primitive corresponding to printf.

You are familiar with side-effects in C, though it is possible that this term was not used to describe them.

Consider what you can do with a function parameter that has been passed by reference: from inside the function, you can change the ('external') value of that parameter.


key: chroma:document
string_value:
Such a change is said to be a side-effect of the function call. 

(x:x) There is no requirement that all of the formal parameters actually occur in the lambda body. Eg 

((lambda (x y) x) 17 18) 

makes perfect sense, and returns 17. 

Indeed, there is no requirement that ANY of the formal parameters occur in the function body. Eg


key: chroma:document
string_value:
((lambda (x) 3) 8) will return 3. This is said to be a constant function. Perhaps a better way to write such a function might be (lambda () 3) with calls having 0 actual parameters. Note also that we may not write lambda forms with duplicated parameters - e.g. (lambda (x x) x) is not accepted by the reader.


key: chroma:document
string_value:
(****) I will use standard terminology to describe parameters: in the call ((lambda (x y) (+ x y)) 2 3) x and y are the formal parameters and 2 and 3 are the actual parameters (****) The number of actual parameters is set by the definition of the function, and any call with a different number of actual params than the number of formal params will be flagged as an error. This is true for C++, as well; it is a type error.


key: chroma:document
string_value:
CSC335 Class 12 March 7 2024
Quiz 2
(Full Period)

CSC335 Class 13 March 12 2024
Towards Higher Order Procedures


key: chroma:document
string_value:
; Consider the summation procedure sigma: 
; pre: a and b are integers 
; post: returns ∑ i from a to b of i 
(define (sigma a b) 
(cond 
((> a b) 0) 
(else (+ a (sigma (+ a 1) b))))) 
; this is a recursive program, but the induction is a bit different from what we have seen so far 
; in that we will induct on the size of the gap between a and b: 

Why? Suppose we were to say in 
proof by induction 
on a "program is 
that a is increasing 
and so the 
program is 
smaller than 
provided a<b, the gap between 
and b. So this METRIC is variable. 
would be for 
for an induction. 

Your documentation should include 
The definition you have in mind for 
gap(a,b) 

Suggested hw

Would ((= a b) 0) be better? 
Well, NO: when a = b, ans' needed for a.


key: chroma:document
string_value:
Code for this design is easily seen to be

(define (sigma a b)
  (cond ((> a b) 0)
        (else (+ a (sigma (+ a 1) b)))))

Might we instead keep a fixed and induct on b?
Work out the details!
Work out the program!


key: chroma:document
string_value:
Higher-order functions — some motivation

We've discussed :

(define (sigma a b)
  (cond ((> a b) 0)
        (else (+ a (sigma (+ a 1) b)))))

and one notices that this pattern occurs frequently. For example, we can compute

∑i from a to b i

using

(define (sum-of-squares a b)
  (cond ((> a b) 0)
        (else (+ (square a) (sum-of-squares (+ a 1) b)))))

Also -

(define (sum-of-cubes a b)
  (cond ((> a b) 0)
        (else (+ (cube a) (sum-of-cubes (+ a 1) b)))))

When a functional programmer detects a pattern like this, he/she asks whether one program might be made to do
The work of all three.

In fact, this is easy to do: one introduces a function
parameter and calls the new function with values
(lambda (x) x), square, and cube for that parameter


key: chroma:document
string_value:
Define generalized-sigma a term b)
(cond ((> a b) 0)
      (else (+ (term a)
            (generalized-sigma (+ a 1)
                               term
                               b)))))

Now we obtain sigma using generalized-sigma

(define (sigma a b)
    (generalized-sigma a (lambda (x) x) b))

and sum-of-squares

(define (sum-of-squares a b)
    (generalized-sigma a square b))

and sum-of-cubes

(define (sum-of-cubes a b)
    (generalized-sigma a cube b))

This is one nice way of achieving software reuse -
here, for example, only generalized-sigma would
need to be stored in a function library, rather
than sigma, sum-of-squares and sum-of-cubes


key: chroma:document
string_value:
generalized-sigma is said to abstract the functions sigma, sum-of-squares, and sum-of-cubes.

Is this all there is to do? Does introducing this new variable have any impact on the proofs we write? Looking at the code you can see that it does – we need to check the interface between term and generalized-sigma.

(define (generalized-sigma a term b)
  (cond ((> a b) 0)
    (else (+ (term a)
              (generalized-sigma (+ a 1) term b)))))

You can see that term needs to be a function from [a, b] → Numbers – otherwise makes no sense.

Moreover, you can see that (term i) needs to terminate for each i in [a,b] - for otherwise we cannot make any promises about the termination of generalized-sigma.

So we should perhaps look at how one would write a proof for a function with a function parameter.


key: chroma:document
string_value:
Just as we want our function library to contain just the most general version of sigma, so also would we like to write just one proof. In particular, we do not want to be forced to write a proof for each possible value of term. (Can you see other values of term which would be acceptable? fourth-power? Maybe sqrt? Maybe ... our catching that pattern is very much more powerful than just generalizing the functions we started with) Can you see what the qualifications for term values must be? Those qualifications are precisely the specification for term; if the proof we file is to be as useful as the function, then the pf should not refer at all to particular choices for term — e.g. not to square or to cube — but just to the specification of term. In other words, we want the proof to be abstract in the same sense that the code is abstract.


key: chroma:document
string_value:
Let's sketch a proof for generalized n-sigma
define (generalized-sigma a term b)
  (cond ((> a b) 0)
        (else (+ (term a)
                (generalized-sigma (+ a 1)
                               term
                               b))))
As for sigma (see earlier notes), we can
organize the proof as an induction on gap(a, b)

where gap(a, b) = { 0 if a > b
                    b-a otherwise

We focus just on the nth :

'The recursive call works correctly,'
ie

given the pre-conditions
(generalized-sigma (+ a 1) term b)
correctly computes

     b
   Σ term(i)
   i=a+1


key: chroma:document
string_value:
For this to make sense, term must be compatible with + (i.e., Σ) and the values term(i) must all be defined (i.e., term i) must terminate.

These are the ONLY requirements we impose on term.

The spec for generalized-sigma has - in the spec for sigma - requirements for term

pre: a ≤ b and a, b are integers
    and
    for each a≤i≤b, term(i) is
    compatible with +

post: returns Σ term(i)
           i=a

Termination argument also requires that (term i) returns a value for each i ∈ [a, b]


key: chroma:document
string_value:
To put this another way - the user who wishes to use generalized-sysm must guarantee as b , a and b integers, AND that term has the properties required by the precondition. All of square, cube, fourth, sqrt -- have those properties. Notice that our proofs - both of partial correctness and termination - rely on the fact the evaluating (term a) does not change a or b. We know this because our functions have no side-effects. In C, you'd need to add this requirement to the precondition.


key: chroma:document
string_value:
We can go even further with this usage of function parameters. Suppose, for example, that we wanted to compute the sum of the odds between a and b. We'd need of course to worry about the parity of a and b, but surely there is no need to lock a up by 1 at each step — why not have a step size of 2? This suggests

(define (even-more-general-sigma a term b next)
  (cond ((> a b) 0)
    (else (+ (term a)
      (even-more-generalized-sigma
        (next a) term
        b next)))))


key: chroma:document
string_value:
where a possible value for next is

(lambda x)(+ x 2)

You should develop a specification for the function
next - and indeed, for this entire procedure.

Pay particular attention to the requirements
imposed by termination!

For example, you might want to require
that gap (next (a), b) < gap (a,b)

But in fact - though the mechanism would need to
be adjusted - all you would need is that
next reduces the gap after a bounded
number of calls...


key: chroma:document
string_value:
Now consider an iterative version

(define (sigma a b)

(define (iter a result-so-far)
(cond ((= a b) result-so-far)
(else (iter (+ a 1) (+ (+ a 1) result-so-far))))))

(cond ((> a b) 0)
(else (iter a a))))

; with guess-invariant for iter as follows:
; a <= b AND result-so-far = sum of all integers i
; from A up to a, A <= i <= a, where A is the
; original value of the parameter a


key: chroma:document
string_value:
Let's check the G_i:
strong_enough? When a=b, this sum result_so_far is the sum from A up to and including b - which is correct.
weak_enough? We need to check the fact that ∑ from i=a to a is a and check if this is true.
Is it preserved? Yes, given the code and the fact that ∑ from i=a to b is a + ∑ from i=a+1 to b.

Suggested Exercise
What changes are needed if result_so_far is initialized to 0 rather than a?


key: chroma:document
string_value:
We can go even further with this usage of function parameters. Suppose, for example, that we wanted to compute the sum of the odds between a and b. We'd need of course to worry about the parity of a and b, but surely there is no need to lock a up by 1 at each step — why not have a step size of 2? This suggests
define even-more-general-sigma
(an term b next)
(cond ((> a b) 0)
(else (+ (term a)
(even-more-generalized-sigma
(next a) term
b next))))


key: chroma:document
string_value:
where a possible value for next is
(lambda (x) (+ x 2))

You should develop a specification for the function
next - and indeed, for this entire procedure.

Pay particular attention to the requirements
imposed by termination!

For example, you might want to require
that gap (next (a), b) < gap (a, b)

But in fact - though the reduction would need to
be adjusted - all you would need is that
next reduces the gap after a bounded
number of calls...


key: chroma:document
string_value:
Class 19 April 2024

Q: append & reverse
; append - a primitive

(define (myappend l m) 
  (cond ((null? l) m)
    (else (cons (car l) (myappend (cdr l) m)))))

example: (myappend '(1 2) '(3 4 5)) = (1 2 3 4 5)

\(obugrac\): '\(l\)' is the zero -> \(1e

(myappend '()' '(3 4 5))=
\(\upppa\)\(3 4 5\)'\(l\) = (3 4 5)

not set union - look in \(\ads\) ch 2 for discussion of
representing sets as lists without duplicates.. and find
observe (coppend '(1 2) '(3 5)) = (1 2 3 5).

observe: cdrs down first list, does nothing
with the second list.

* Weirdness: in fact, the second argument can
be any scheme object - not required even to be
a pair.

Runtime: 
Increase in the length of the first argument
Can the efficiency be improved? - e.g. by selecting
the shorter list for cdring down. Possibly, depending on
the shorter list for cdring down. Possibly, depending
on various factors.


key: chroma:document
string_value:
on the data set, might it be worth inverting the cost of computing (length l) and (length m)? Let’s think about how this might work - noting that the result we want is not

we might try to build a solution using snoc :
(define (snoc e l)
  (cond ((null? l) (list e))
    (else (cons (car l)
                (snoc e (cdr l))))))

Clearly, we can compute (append l m) by “snocing each element of m down l.” But could this ever be more efficient than the design we gave first regardless of the relative lengths of l and m? I’ll leave this for you to think about.


key: chroma:document
string_value:
We sketch an inductive proof for append ...

What about an iterative append?
(define (append-iter l m)
(define (iter remaining so-far)
(cond ((null? remaining) so-far)
(else (iter (cdr remaining)
(cons (car remaining) so-far)))))
(iter (reverse l m)))

GI: append l M) =
(append (reverse remaining) so-far)

Finally, for append, we note
Algebraically - append is NOT commutative ~
(append l m) ≠ (append m l)
Algebraically - append IS associative


key: chroma:document
string_value:
; reverse - another primitive in R^5 R^s

Recursive;
(define (myreverse l)
  (cond ((null? l) l)
    (else (append (myreverse (cdr l)) (list (car l))))))

work out na pf
1+; rec. call
returns the reverse
of (cdr l)

observe

main functions

reverse L E append reverse (cdr L) list (car L)

Feel free to use the main functions of the same names as your scheme functions in your writeups.

; quadratic time, and we wonder whether we can do better

Does everyone see this? If |L|=n, then
The first call to append needs time (n-1),
The second needs time (n-2), and so on.
You recall Σ (i = 1 to n-1) i = O(n^2)


key: chroma:document
string_value:
(define (myreverse l)
(define (iter l result)
(cond ((null? l) result)
(else (iter (cdr l) (cons (car l) result)))))
(iter l '()))
;linear time, so better

GI: reverse(L) = append((reverse (cdr l)), result)

This GI is of the form
total work = work remaining + work done.


key: chroma:document
string_value:
2. note that R5RS's map primitive is more powerful than mymap - map - a function compatible with elts of the list Q (define (mymap f l) (cond ((null? l) l) (else (cons (f (car l)) (mymap f (cdr l)))))) (define (square x) (* x x)) ;examples (mymap square (1 2 3 4)) = ((square 1) (square 2) (square 3) (square 4)) -> (1 4 9 16) observe: (mymap f Q) = l & map does not change l Q B. order is preserved - i.e. - the i-th element of the returned list is (f l_i ), where l_i is i-th elt of the input list. C. Arena time if f works in constant time - more generally, if f of l_i is constant for each l_i, then the time needed is time (f Q) * |Q|.


key: chroma:document
string_value:
3. accumulate
(define (accumulate op init seq)
  (cond ((null? seq) init)
    (else (op (car seq) (accumulate op init (cdr seq))))))
; accumulate is also known by the name foldr
; examples ...
(accumulate + 0 '(1 2 3 4)) =
(+ 1 (accumulate + 0 '(2 3 4)))
returns (+ 2 (+ 3 (+ 4 0)))
returns
(+ 1 (+ 2 (+ 3 (+ 4 0))))
(accumulate * 1 '(2 3 4)) =
4
I = 1
note that the initial values can (and need to be) different.
(accumulate cons '() '(1 2 3 4)) =
(1 2 3 4)


key: chroma:document
string_value:
(accumulate append '() seq) ... ->

What type is seq?

Got to be a list of elements compatible with append -
ie - a list of lists.

(accumulate append '() '((1 2 3) (4 5) (6)))

↓

(1 2 3 4 5 6)

We say that the input list had been flattened.

Try: (accumulate append '() '((a b c) '(1 2 3) (4 5)))

We've changed int

Try: (accumulate append '() '('((1 2 3)) ((4 5)) (6)))

fr (define (flatten list-of-lists)
   (accumulate append '() list-of-lists))

You'll see that flatten 'promotes' each sublist by 1 level (think of trees)


key: chroma:document
string_value:
Note that accumulate does not necessarily preserve the length (etc) of the input list. It's not even required to return a list.

; examples ...
(define (mymap f l)
  (accumulate (lambda (x y) (cons (f x) y)) '() l))

;; illuminating
(define (leftmost lst)
  (foldr (lambda (x y) x) '() lst))

Can you apply the same reasoning to explain why this is called leftmost?

; may computing
(accumulate (lambda (x y) (+ x y)) 0 '(1 2 3 4))

This is just the binary specialization of +

Same as
(accumulate + 0 '(1 2 3 4))


key: chroma:document
string_value:
Subbing function to fold:

; accumulate-left, or fold

(define (accumulate-left op init seq)
  (define (iter acc rest)
    (if (null? rest)
        acc
      (iter (op acc (car rest)) (cdr rest))))
  (iter init seq))

; examples ...

(define (elem e l)
  (accumulate-left
    (lambda (acc first) (or acc (eq? first e)))
    #f
    l))

(elem 3 '(1 2 3 4 5))
(elem 6 '(1 2 3 4 5))

; check out the symmetry - compare leftmost, above

(define (rightmost lst)
  (accumulate-right (lambda (x y) y) '(0) lst))

acc initially #f, so first: (or #f (eq? 3 1))
so on the second call to iter, acc: (or #f #f)#f
and on the third call, acc: (or #f (eq? 3 2))#f
and on the fourth call, acc: (or #f ((eq? 3 3))#t

and once acc is #t, its value does not change
(even though we don't have early exit)

Why bother with the folds? Their importance and usefulness are hard to overstate; virtually all
functions which process lists' elements-by-element
can be realized as folds.


key: chroma:document
string_value:
Filter
not a primitive boolean function, compatibility is required
Just
(define (filter pred seq)
  (cond
    ((null? seq) seq)
    ((pred (car seq)) (cons (car seq) (filter pred (cdr seq))))
    (else (filter pred (cdr seq)))))
; examples...
(filter odd? '(1 2 3 4)) - (1 3)
Iterative or recursive?
Recursive, because the stack might be used.

suggested for memorization
- accumulate
- filter

You need these at your fingertips
if they are to be useful in
conceptualizing problem
solutions.


key: chroma:document
string_value:
Brief comment on the replace-nth occurrence problem (of old by new). 

Two cases - either an occurrence occurs in the car of t or occurs in the cdr. 

cdr is the interesting case since there is no way for the recursive call on the car to communicate to the recursive call on the cdr the number of olds which were found in the car. 

The nth occurrence of old in t is the 2nd occurrence of old in (cdr t). 
 
Hint: count the number of occurrences of old in the car (in a separate tree traversal) - and adjust the replacement index for the cdr call accordingly. 

If we had assignment to a mutually visible global variable, this would be trivial.


key: chroma:document
string_value:
CSC 335 Class 18 April 2 2024

1. Resume with subst* as an example of tree recursion.
Replace each occurrence of old by new (both old and new are atoms)

- a pattern in which the recursive calls are on the car and cdr
- The divide and conquer decision is to work on car and the cdr separately

define (subst* tree old new)
(cond ((null? tree) tree)
      ((atom? tree) (if (eq? tree old) new tree))
      (else (cons (subst* (car tree) old new) (subst* (cdr tree) old new))))

What to do with the results of the recursive calls?
By this point, we know subst* is to return a tree. And from that we know that the recursive calls return trees, so the question becomes: 'How do we put two trees together to get another tree.' The only option: cons.


key: chroma:document
string_value:
Since car tree and cdr tree are proper components of tree, we may assume that the recursive calls work.

A restricted form of equal? introduced as another example of tree recursion.

(define same-shape?
  (lambda (l1 l2)
    (cond
      ((or (not (pair? l1)) (not (pair? l2))) (eq? l1 l2))
      (else
        (and (same-shape? (car l1) (car l2))
             (same-shape? (cdr l1) (cdr l2)))))))

The goal here is to identify when l1 and l2 have the same structure as trees. Their cars must recursively have the same structure, and their cdrs must recursively have the same structure.

If either l1 or l2 is not a pair, then they must be eq?. 

e.g. If l1 = 2 and l2 = '(2) then (eq? l1 l2) => #f and surely l1 and l2 have different structures.

But stronger, since eq (same-shape? a b) is #t for atoms a and b only if (eq? a b).

So (same-shape? (subst x a b)) = #f


key: chroma:document
string_value:
Even though t and (substituting t a b) are 'tree-isomorphic'—have the same shape as trees at every level—they are not identical. Wait a minute! For t1: and t2:, we do have (same shape? t1, t2) = #t, even though t1 and t2 occupy different memory locations (i.e., (eq? t1 t2) = #f). So we mean logically identical, as opposed to physically identical. This is what eq? worries about.


key: chroma:document
string_value:
A simple interpreter — simple, yes, but it serves as a template for all interpreters yet to come in 335.. an evaluator. The goal is to provide an evaluator for a class of algebraic expressions; i.e., for a particularly simple programming language. As you might expect, we begin by defining the syntax of the programming language — i.e., by defining the class AExp of algebraic expressions. I say this is expected — because once we have the syntax — the structure of AExp — we more or less automatically know how to write the interpreter/evaluator/calculator. Let's say that AExp is the least class containing non-negative integers which is closed under the operations of +, *, and !, defined as follows: if e1 ∈ AExp and e2 ∈ AExp, then


key: chroma:document
string_value:
(e1 @ e2) ∈ Aexp
(e1 # e2) ∈ Aexp
(e1 ! e2) ∈ Aexp
This is just syntax! Nothing at all has been said about the meanings of @, #, !
Some examples of expressions belonging to Aexp
0, 1, 2, ...
(1 @ 2), (1 # 2), ...
(1 @ (1 # 2)), ...
In BNF, Aexp ::= non-terminal scheme infix
(Aexp @ Aexp)
(Aexp # Aexp)
(Aexp ! Aexp)


key: chroma:document
string_value:
Going back to our discussion of structural induction
The components of, say
((1 1 (2 # 3)) @ (4 1 5))

and;
1, 2, 3, 4, 5
((1 1 (2 # 3))
(2 # 3.)
(4 1 5)

Note that ((1 . . . is NOT a component - components must themselves be well-formed - ie - must belong to Aexp.

It's useful to set up Aexp as a data structure.
Data structures have constructors, selectors, and classifiers - here, we use make to define a user-interface:

constructors:
(define (make-@ el e2))


key: chroma:document
string_value:
(list e1 '( e2))
(define (make-# e1 e2)
(list e1 # e2))
(define (make-! e1 e2)
(list e1 ! e2))
Selectors
(define (first-operand e)
(car e))
(define (second-operand e)
(cadr e))
(define (operator e)
(car e))


key: chroma:document
string_value:
classifiers
(define (#-exp? e)
  (eq? (operator e) #))
(define (@-exp? e)
  (eq? (operator e) '@))
(define (!-exp? e)
  (eq? (operator e) '!))


key: chroma:document
string_value:
CSc 335 Class 18 Part 2 April 4 2024

An interpreter for Aexp using the Aexp data structure, as well as tree-recursion. This is parameterized: inputs e - Aexp. This outputs the correct value of e for or a higher postconditions. The definitions of the operators @, #, ! because it has function parameters. We name the interpreter value - The interpreter is the entity which defines the semantics (i.e., the meaning of expressions in Aexp.

Define value =
(lambda (aexp) (cond ((actnum? aexp) aexp)
(else cond ((@-exp? aexp) (+ (value (first-operand aexp))
(value (second-operand aexp)))),
 ((#-exp? aexp) (# function))))

This is an example of interpretation by recursive


key: chroma:document
string_value:
descent 
(value (first-operand aexp) 
(value (second-operand aexp))))

((l-exp? aexp)
(l-function
(value (first-operand aexp))
(value (second-operand aexp))))

What are some possible definitions for @-function and !-function?

While we could simply 
define @-function + 
define #-function * 
define !-function expt

It is important - and demonstrative of the basic power of interface technology - to realize
that these functions could be defined arbitrarily
without the constraints imposed by the value
function. That is: They each need to be binary
functions.


key: chroma:document
string_value:
functions returning numbers.

Even this could be refactored
if we changed the basis step
to return some type other
than scheme-number.

In my notes you will find my-plus, my-times, my-=rest
and you should to produce other examples on your
own. To that end, I ask you to spend some time
with the group exercise of HW 7. I ask you
to discover and carry out a minimal set of
modifications on the value function to allow
to deal with Axiom modified so that its
basis code is simply lists of ones. E.g.

(1)@(1) = (1)

(1)@(1,1) = (1,1,1)

IE - simple 'stroke notation'. One goal is to


key: chroma:document
string_value:
NOT use base-10 numbers anywhere in your design. Another is to preserve data abstraction - you should not 'reach behind' the interface functions to work directly with the low-level representations.

To solidify your understanding of function parameters, you should also rewrite the value function so that it takes a @-function, #-function and $-function as parameters. Notice that doing so brings the function into alignment with the spec we gave.

Can we in fact make even greater use of higher-order functions to further improve the value function?


key: chroma:document
string_value:
Now consider the following function:
(define atom-to-function
  (lambda (x)
    (cond ((eq? x 't) t-function)
          ((eq? x '#) f-function)
          (else l-function))))

We can use this to improve value:
(define value
  (lambda (aexp)
    (cond ((anatum? aexp) aexp)
    (else
      (atom-to-function (operator aexp))
        (value (first-operand aexp))
        (value (second-operand aexp))
    ))))


key: chroma:document
string_value:
CSc 335 Class 20 April 9 2024

; enumeration
(define (enumerate-interval low high)
(if (> low high)
'()
(cons low (enumerate-interval (+ low 1) high))))

(define (enumerate-tree tree)
(cond ((null? tree) '())
((not (pair? tree)) (list tree))
(else (append (enumerate-tree (car tree)) (enumerate-tree (cdr tree))))))

; previously called this
; fringe

.Lets look at 2 versions of postorder for
enumerate-tree. The first.

; post : returns list of the leaves of tree.
; (no mention of the order of these)

; post2 : returns the list of the leaves of tree in
; the same order as they occur in tree.

I want to look at proofs appropriate for each version

Both are tree recursions so we know we need to carry
out tree inductions.

HH : the recursive calls each return a list of leaves,

; one of the leaves in (car tree) and the
; other of the leaves in (cdr tree).


key: chroma:document
string_value:
Is; given two lists, we see that append is the right combiner since (append l1 l2) is just the concatenation of l1 and l2.

But the program as written actually satisfies the stronger specification given with post. The reason for this, of course, is that append preserves the element-wise order of its arguments.

So the question is: can the program be made more efficient or more clean if we used a combiner other than append?

For example, if we require that a tree not have duplicate leaves, so that the fringe is a set instead of a multiset, perhaps some version of set-union could be more efficient than append - or without making the program perhaps multiset union could be useful.

The point I am making is that the program is overspecified relative to post, - suggesting that one wants to consider alternatives.


key: chroma:document
string_value:
This kind of thing happens frequently – consider, for example, the gap between many graph algorithms and their implementations. Spanning Tree Algorithm implementations with unprocessed nodes stored on a heap implementations with unprocessed nodes stored on a stack. One could say that when the algorithm states, at a high level of abstraction, 'select an unprocessed node' that it actually doesn't care how this selection is done. That the algorithm is non-deterministic. Non-determinism is actually used by all of us in thinking – at a high level – about computational  processes. Consider, for example, the process


key: chroma:document
string_value:
of sending email from point A to point B. High-level view A -> B In reality, we have no idea (without using your favorite net utilities) what happens. The message makes A -> A1 -> A2 -> ... -> AN -> B on a day with light traffic. But maybe A -> B1 -> B2 -> ... -> BN -> B on an entirely different set of crowded time. Yet, the behavior - at the top level - appears deterministic.


key: chroma:document
string_value:
The convenience of this kind of abstraction is evident – and very much the kind we seek in functional programming. So: are nondeterministic programming languages a thing? YES – eg. see (part of) Ch. 4 in A&S, where they build an interpreter for one. See also the A&S discussion of amb, which was used by McCarthy (inventor of LISP) for nondeterministic programming. As the not-example suggests, what happens non-det. at one level must be in deterministic at a lower level. So amb is actually nothing more than a one-word trigger for BFS. You'll see some amazingly elegant solutions to puzzles using amb in A&S.


key: chroma:document
string_value:
; signal processing approach to some problems

(define tree-2 (list 1 (list 2 3) (list 4 5)) (list (list 6)))

(define (sum-odd-square-tree)
  (accumulate +
              0
              (map square
                   (filter odd?
                          (enumerate-tree tree)))))

(define (fib-n)
  (define (aux curr prev count)
    (if (= count 0)
        prev
        (aux (+ curr prev) curr (- count 1)))))
  (aux 1 0 n))

(define (even-fibs n)
  (accumulate cons
              '()
              (filter even?
                     (map fib
                          (enumerate-interval 0 n)))))

(map by
  (0 1 2 ... n fibs))
(filter by even
  (0 2 ... just even fibs out to n))
accumulate cons ??

(0 2 ... list is unchanged)

This is the returned value proving that the program returns progressively
lengthening list consisting of precisely
fib #'s up to fib n.

Why is this a proof? There is no recursion or iteration at the top level - in fact, we have already


key: chroma:document
string_value:
proved the correctness of map, filter, accumulate -> SO THERE IS NO NEED TO DO IT AGAIN! 
All one needs to do is to describe the data flow (after checking that the recomposed functions are compatible), perhaps with a diagram (as I have done).


key: chroma:document
string_value:
IE, nested for-loops in Scheme

; nested mappings

; given a positive integer n, find all ordered pairs of distinct positive integers i and j,
; where 1 <= i < j <= n

(define (ordered-pairs-of-distinct-integers n)
  (accumulate append
    '()
    (map (lambda (i) (map (lambda (j) (list i j))
                          (enumerate-interval (+ i 1) n))))
    (enumerate-interval 1 (- n 1)))))

Realize that - once again - there is no top-level recursion or looping. So our interest is enrafted
at the data flow or for the previous example , so that the code appears

(map (lambda (i) (list i))
     (enumerate-interval 1 (- n 1)))

It's easy to see that the flow is

(1 2 ... n-1) -> ((1) (2) ... ((n-1) n))

Now we look at (f i) - eg
(f 2) -> ((1 2) (1 3) ... (1 n))
       -> ((2 3) (2 4) ... (2 n))

and so on. So when this completes,


key: chroma:document
string_value:
You have (1 i) replaced by ((i i+1) (i i+2) ... (i n)) and thus the intermediate output is a list of lists of pairs ((1 2) ... (1 n)) ((2 3) ... (2 n)) ... ((n-1 n)) Problem output is a list of pairs, not a list of lists of pairs - so we flatten using the usual accumulate + append combination.


key: chroma:document
string_value:
; flatmap allows a simplification

(define (flatmap proc seq)
  (accumulate append '() (map proc seq)))

(define (ordered-pairs-of-distinct-integers n)
  (flatmap (lambda (i)
            (map (lambda (j) (list i j))
                 (enumerate-interval (+ i 1) n)))
           (enumerate-interval 1 (- n 1))))

; flatmap turns out to be quite useful - next we use it to compute
; all permutations of a set S

; for example, the permutations of {1,2,3} are given --
; first, list all permutations with 1 in the first position
; next, list all permutations with 2 in the first position
; finally, list all permutations with 3 in the first position

(define (permutations s)
  (if (null? s)
      (list '())
      (flatmap
       (lambda (x)
         (map (lambda (p) (cons x p))
              (permutations (remove x s))))
       s)))

; where

(define (remove item sequence)
  (filter (lambda (x) (not (= x item)))
          sequence))


key: chroma:document
string_value:
Class 21 CSc 335 April 11 2024

2nd Interpreter of the Term

(Due to Friedman and Felleisen, the authors of TLS)

An L-expression is an S-expression which is either:
(AND 1 2), or
(OR 1 2), or
(NOT 1 1), or
an arbitrary symbol, which we call a variable

Here 1 and 2 are arbitrary L-expressions, so this is an inductive definition once we add 'and nothing else is an L-expression'

(a) Write and certify a function lexp? which checks whether an S-expression is an L-expression.

(b) Write and certify a function covered? of an L-expression lexp and a list of symbols los that tests whether all the variables in lexp are in los.

(c) For the evaluation of L-expressions we need association lists, or alists. An alist for L-expressions is a list of (variable, value) pairs. The variable component is always a symbol, and the value-component is either the number 0 (for false) or 1 (for true). Write and certify a function lookup var the symbol var and the association list al, so that (lookup var al) returns the value of the first pair in al whose car is eq? to var.

(d) If the list of symbols in an alist for L-expressions contains all the variables of an L-expression lexp, then lexp is called _closed_ with respect to this alist. A closed L-expression can be evaluated essentially by substituting the values of the variables given in the alist for the variable occurrences in the L-expression. You are asked to write and certify the function value of an L-expression lexp and an alist al, which, after verifying that lexp is closed with respect to al, determines whether lexp means true or false. If lexp is not closed wrt al, then (value lexp al) should return the symbol not-covered.

 (a) comprises the pre-evaluation sanity check

 (b) and (c) comprise the environment subsystem


key: chroma:document
string_value:
(b)  Define covered? (exp pos)
    (cond ((symbol? exp) (member? exp pos))
          ((member? 'op exp) '(and or))
          (and (covered? (first-arg exp) pos)
               (covered? (second-arg exp) pos)))
    (else (covered? (first-arg exp) pos)))
This is an example of recursive descent exploring the inductive structure of EXP.

(c)  An example a-list might be
    ((x 0) (y 1) (z 1))
    i.e., a flat list of bindings. There's no restriction on the number of times a given variable can occur —

eg ((x 0) (y 1) (z 1) (z 0))
    rs also an a-list.

    The lookup function will ignore all but the first binding for a variable.

(define (lookup var a-list)
    (cond ((eq? var (car (car a-list))) (cadr (car a-list)))
          (else (lookup var (cdr a-list))))
This returns the value from the first (leftmost) binding of var.


key: chroma:document
string_value:
Note that (AND x y) means one thing if a list is ((x 0) (y 1)), and means something else if a list is ((x 1) (y 1)) explains the need for the alist argument.

CD) We separate checking from evaluation - we are assuming pre for value is satisfied - ie 
lexpr really is in lexp and also that 
covered? leXp (map can alist)) is true.

(define (value lexp alist) represents this systems
alist is this systems representation of environment.
(cond ((symbol? lexp) (lookup lexp alist))
((and? lexp) 
(cond (value (first-arg lexp) alist) 
(value (second-arg lexp) alist))

(or? lexp)

(not? lexp)))

. Define (check-and-evaluate lexp alist)
(cond ((not (lexp? lexp)) syntax-error)
((not (covered? lexp(map can alist)))
not-covered)


key: chroma:document
string_value:
; pre for value guaranteed at this part.
(else (Value ReqQ aList)))

It will be useful to notice that a list can be thought
of as a stack -

list stack
cons push
car top
cdr pop

(The left end of the list is the top of the stack)

We will make use of this to implement local
variables (and, generally, scoped) in the TES
interpreter -> ch 10 of the Little Schemer ->
starting next.

Note that all variables in the LEXD system
are global

(Historical reference: This was true of BASIC in the
1960s as well as of the first versions of
FORTRAN)


key: chroma:document
string_value:
For scheme, we need somewhat more complex environment structures.

To see this, let's consider some lambda forms and their associated environments

Example 1
(lambda (a b) (+ a b)) 
2 3

The expression (+ a b) is evaluated in this environment

(a b)----(2 3)

Essentially the same flat structure used for L-exps, perhaps represented as a pair of lists -- the first is a list of names, i.e., (a b), and the second is the list of corresponding values, (2 3)
The lookup procedure here would be to simultaneously cdr down the vars and values lists

But this simple one-rib environment is not
always adequate

Consider:


key: chroma:document
string_value:
; example 2

((lambda (a b)
  (+ a b (+ (lambda (a c) (+ (+ a c) b))
    3 4)))
  1 2)

; the expression (+ (* a c) b) is evaluated in this environment

; (a c) ---- (3 4)
; (a b) ---- (1 2)

; and can be seen to have values a=3, b=2 and c=4. The search for a given symbol begins with the rib containing the symbol; it proceeds to the 'outer' rib only if the symbol does not occur in the first rib. What happens if it does not occur in any rib?

; when this expression has been evaluated, the outer expression (+ a (* b 14)) is evaluated in this environment.

; (a b) ---- (1 2)

; to give (+ 1 (* 2 14)) = 29

; these environment structures are usually described as 'rib-cage environments': you can see the ribs, like a sternum, suggested in our diagrams

; clearly, an interpreter is going to need to 'push' and 'pop' ribs at appropriate times.


key: chroma:document
string_value:
Wake Up Class 4/10/24 (by Zoom)

HW 2 - #2
Recursive list-ref

pre: L is a list, and j is an index into that list
post: (list-ref L j) returns the index j'th of L

idea: defer cdr - we'll have a wrapper, but the recursive function will assemble a stack of cdrs.
For example:
((cdr(cdr(cdr '(1 2 3 4 5)))) = an '(4 5)' = 4 (index 3 elt)

define (rec L j)
(cond ((zero? j ) (car L ))
(else (cdr (rec ?? ...) ))

Looks wrong. So let's try moving on to the wrapper.

define (rec L j)
(cond ((zero? j) L)
(else (cdr (rec L (- j 1)))))

want to return cdr(cdr...(cdr L))...
j cdrs

What are we recursing on? (clearly not L)


key: chroma:document
string_value:
So - 
Det: to apply cdr j times to l,
we apply cdr to (cdr^j-1 l),

we need a wrapper.

(define (myListRef l j)
  (car (rec l j)))

Thus we return the index j element of l.

Check: has the 0-based indexing been
correctly worked in?

Yes - by induction on j - {work this out.}

Notice that rec itself does not cdr down l in the usual sense -
rec returns the result of applying cdr j times to l.


key: chroma:document
string_value:
Let's take another look at this problem. A problem from [ABS ch 1] asked us to write a high order function which inputs 2 composable functions f and g and which returns the composed function λx. f (g x) using lambda.

For example, (compose square cube) would be called as (compose square cube) 2 giving 64.

; Pre: f and g are composable
(define (compose f g)
  (lambda (x) (f (g x))))
; Post: returns λx. f (g x).


key: chroma:document
string_value:
And a related problem is (iirc)

Write a function, repeated, which works

((repeated square 2) 5) = 625

(define (repeated f n)
   (cond ((zero? n) (lambda x x))
     (else (compose f (repeated f (- n 1))))))

pf by induction on n

You should rework your solutions to HW using repeated(). It might not apply to all of those problems, but it certainly applies to a number of them.

Let's revisit the recursive list-ref...

we have

(define (rec a i)
   (cond ((zero? i) a)
     (else (cdr (rec a (- i 1)))))


key: chroma:document
string_value:
(define (alt-rec i j)
  (if (null? i) j
      (appulted-cdr j)))

This returns the functim

λx.(cdr x)

Which is not what rec desired. Can
we change the wrapper to make this
compute lst-ref?

How about you all working this out?


key: chroma:document
string_value:
CSC 335 Class 16 March 26 2024

;; list ::= () | (cons atom list) | (cons list list))
;; lat ::= () | (cons atom lat)

We want a function member? which inputs a lat and an atom and which checks whether the atom is included in the lat.
(Give a recursive development)

Our divide & conquer strategy is simply to cut down the input list;
- we process the car explicitly
- we let the recursive call 'magically' process the cdr
- we combine those results to solve the problem

Focus on *ie* - on the recursive call

We expect the recursive call to completely solve the problem for the cdr.
We need to write down precisely what


key: chroma:document
string_value:
this means. for this problem:

The recursive call returns #t when the input atom occurs in the cdr, and #f otherwise.

Since a occurs in mlat precisely when it is either (car mlat) or it occurs in (cdr mlat), we see how to write the code

(define (member? a mlat)
  (cond ((null? mlat) #f)
        (else (or (eq? a (car mlat))
                  (member? a (cdr mlat))))))

What now does the directive pf look like?

(H: recursive call works if a is an atom and (cdr mlat) is a list)

Of course the Pgm hasn't changed a - so a is an atom here if it was originally.

And we know from the BNF data description that (cdr lat) is a lot. (Note that because mlat is not empty, we know as well that the


key: chroma:document
string_value:
Is this really recursive? Syntactically, it is recursive, with or serving as the ground. But in reality - given the standard left-to-right short-circuit implementation of or - an efficient compiler would likely cast it as a tail recursion. Consider (or x y z) -> returns x if x is #t, with no need to eval y or z. And if x is #f, it simply moves on to y - no need to remember the value of x. But if or happens not to be implemented this way - say it evals right to left - or could indeed use that stack.


key: chroma:document
string_value:
Next let's consider the problem of removing an element from a list.

cdr
cdr-down as our
divide & conquer
strategy
termination is
implicit in cdr-down

can

What do we do in this case to process the
can?

Two cases: either (can inlat) is the
element to be removed, or it's not.

If it is - then - well want - The
Spec seems incomplete; Are we to
remove every occurrence of a, or just
the first occurrence of a?

For E, we need to remove all
occurrences from the cdr as well as
removing the can.

For F, we could just return the cdr


key: chroma:document
string_value:
If it is not, then we have to return it.

So it seems we have a more or less complete design idea; pseudo-code, termination, etc.

Let's have draft code for e

(define (remember* a lat)
  (cond ((null? lat) '())
        (else (if (not (eq? a (car lat)))
                  (cons (car lat)
                        (remember* a (cdr lat)))
                  (remember* a (cdr lat)))))

I am content to talk through the inductive argument - so I can emphasize the tentative nature of the draft code. The role of
the induction is to serve as a check on the code you currently have.

If an error is exposed, then one revisits it.


key: chroma:document
string_value:
The earlier steps. Another way of exposing errors is to run a few tests. In fact, it makes sense to run the tests BEFORE taking the time to write out the induction.

There's another flaw with the spec! It doesn't say ...

why not just return the empty list each time? Certainly that would strip out all occurrences of a !

That the non-a elements have to be retained in the same order they were originally.


key: chroma:document
string_value:
What about option f?

(define (remember a lat)
  (cond ((null? lat) '())
        (else (if (not (eq? (car lat) a))
              (cons (car lat)
                    (remember a (cdr lat)))))))

The inductive argument must involve confirmation for the claim that my code removes the first occurrence of a and no others.

Again - I leave for you the important tasks of testing and writing out the induction.


key: chroma:document
string_value:
Class 17 March 28 2024

Going beyond lists of atoms – how to carry out inductions?

Why not continue inducting on list length? The problem is brought out by an observation and an example:

Observation: If we want to process a list of lists, then it will be necessary to process the sublists as well as any top-level atoms.

Example: (: (1 (2 3 4 5 6) 7)
The entire list has length 3 but the cdr has length 5. If we were to try inducting on length, and if we wanted to use the IH to claim that our program works on all components of the top-level input, we run into trouble because the chosen induction metric us length for the cdr component than for the original input.


key: chroma:document
string_value:
The idea is to find a new induction metric. We will use "Tree complexity", which we need to define.

Tree? What do trees have to do with lists of lists?

It turns out that there is a natural correspondence between these:

S-exp ➜ tree

a ➜ •

() ➜ •

(a) ➜ •
a

(a b) ➜   a
          /   \
          b

(a (b c)) ➜    a
             /     \
          b       c

Add a link to reflect parens


key: chroma:document
string_value:
The ops of car, cdr and cons can be defined on trees also.
(car '(a (b c))) -> a
(cdr '(a (b c))) -> (b c)
(cons 'a '(b c)) -> (a b c)
So you see that we can carry out these operations either in s-exp space or in tree space.
eg (car (b c))
  s-exp -> Tree -> a
           car                   Tree-car
             ↓                          /
            a                a
Similarly for cdr, and for cons.


key: chroma:document
string_value:
The reason I'm showing you this?
Sometimes it is easier (simpler, more clean) to think in terms of trees when we are designing code — and it is necessary to know that this is mathematically justified.

Questions
1) What has happened to 'a' in the tree diagram for 'n'?
(cons 'a 'n) in free space is (tree-cons . a . n) is
2) What does this have to do with induction?


key: chroma:document
string_value:
It gives us a useful metric: we can induct on the "size" of the tree corresponding to the s-expression.

What do we mean by size? Some combination of height and width?

Let's look at our example('1 ('2 3 4 5 6) 7)

In this case the height of the cadr is less than that of the entire tree.

Aside:('1 2 3)
cdr -> ('2 3)
car -> 2


key: chroma:document
string_value:
car -> 3
But height doesn't work for the cdr - 
The cdr has the same height as the entire tree.

Well - what about the number of nodes?
Clearly smaller for each component than for 
the original.

What about the number of leaves? Doesn't work.
(Trade-off, one leaf to one leaf)

Counting nodes is easy in tree space. Since 
we claim to have a correspondence between 
trees and sexps, we should be able to figure 
out what to count in sexp space as well.

What about counting the number of cons cells?
Two questions:(1) is this the same as counting 
nodes?, and (2) does it work ?


key: chroma:document
string_value:
Again consider '( 1 2 3 4 5 (6) 7)'.

'cons cells vs 9 nodes - so the answer to 1 is NO (but won't it always be off by 1, for the empty list?)'

'What about question 2? Looks good'

'Suggested Exercises: Is it true that any well-formed component of a list has fewer cons cells than the list itself? (2) Is the claim true? If so, then counting cons cells is the same as counting tree nodes.'


key: chroma:document
string_value:
Let's look now at an example of a program which works for general s-expression inputs - Direct your attention to lecture9.scm (and to TLS).

We want a program subst* which replaces each occurrence of an atom 'old' by an atom 'new' in the input tree.

We use Tree-recursion - a standard pattern in which one issues recursive calls on both the car and the cdr, after dealing with the basis cases.

That is: The divide and conquer decision is that we will process the car and the cdr separately.

Will pick up here next time.


key: chroma:document
string_value:
Class 05 February 6 and 8 2024
Recursion & Iteration
1. The Visual Distinction Between Recursion and Iteration
(define (fact x)
(cond ((= x 0) 1)
(else (* x (fact (- x 1))))))
function calls itself,
hence syntactically
recursive
Both are
syntactically
recursive
This mult. op. is said to
guard the call to fact
wrapper-only
(define (new-fact x)
(fact-iter x 0 1))
(define (fact-iter x count result)
(if (= count x)
result
(fact-iter x (+ count 1) (* (count 1) result))))
The call to fact-iter
is unguarded.
We need to talk about
what constitutes a
guard
2. Looking more closely, however, you spot a
difference - the recursive call to fact is
guarded
The guard in fact is pointed at-
the cond is NOT counted as a guard. similarly
the if in fact-iter is not a guard.


key: chroma:document
string_value:
let me note that iterative processes are usually referred to as tail-recursive.

Why is this distinction important?

Operational Difference Between Recursion and Iteration (Consequence of the guard or its absence)

(define (fact x)
  (cond ((= x 0) 1)
    (else (* x (fact (- x 1))))))

necessary deferred
until 'fact 5' is
evaluated
effective number
pattern of calls
expansion for
fact 6
(fact 6)
(* 6 (fact 5))
(* 6 (* 5 (fact 4)))
(* 6 (* 5 (* 4 (fact 3))))
(* 6 (* 5 (* 4 (* 3 (fact 2)))))
(* 6 (* 5 (* 4 (* 3 (* 2 (fact 1))))))
(* 6 (* 5 (* 4 (* 3 (* 2 (* 1 (fact 0)))))))
(* 6 (* 5 (* 4 (* 3 (* 2 (* 1 1))))))
(* 6 (* 5 (* 4 (* 3 (* 2 2)))))
(* 6 (* 5 (* 4 (* 3 6))))
(* 6 (* 5 (* 4 12)))
(* 6 (* 5 24))
(* 6 120)
720
The stack contracts
stack initially -> x6
x6
x5
x4
proc.
call
stack
'invlt.
promise'
Once (fact 6) returns
without any further calls, the
system can unwind the stack
 Let's call
this the
'purely recursive'
version


key: chroma:document
string_value:
y carrying over - deferred multiplications - popping each in turn, sometimes I will use the phrases 'proper recursion' where the text uses just 'recursion'. From the pattern of calls expression, ones sees that factorial(n) requires O(n) time for n multiplications and also O(n) space (for the stack). Yes, we will be making use of your background in algorithms (CSE 220 of CUNY). In contrast, the iterative factorial requires O(n) time but only constant space.


key: chroma:document
string_value:
(define (new-fact x)
(fact-iter x 0 1))

(define (fact-iter x count result)
(if (= count x)
result
(fact-iter x (+ count 1) (*(+ count 1) result))))

;; here we see that the work is done by updating the values of count and result

;; (new-fact 6)
;; (fact-iter 6 0 1)
;; (fact-iter 6 1 1)
;; (fact-iter 6 2 2)
;; (fact-iter 6 3 6)
;; (fact-iter 6 4 24)
;; (fact-iter 6 5 120)
;; (fact-iter 6 6 720)

Here, the recursive process is explained with annotations about the flow, specifically noting that this form of recursion does not involve stack expansion and contraction, emphasizing that it operates in constant space by just updating parameters.


key: chroma:document
string_value:
Another way of understanding the difference between (proper), recursion and iteration (tail) recursion comes if you ask "what data needs to be preserved if each process is to be interrupted and then resumed later?" 
For fact -- one would need to save not instruction pointer as well as the entire call stack.
But for fact-iter, the answer is just the instruction pointer and the current values of the parameters.


key: chroma:document
string_value:
So it is usually desirable to have iterative programs.

But you will soon discover that it is dramatically easier to write recursive programs than to write iterative programs.

Since human time is almost always the really expensive part of software... one wants to understand recursion as well as iteration.

Think: rapid prototyping

(As an aside - gcc has a switch allowing users to optimize for tail recursion; smart enough to avoid using a stack when this is unnecessary)


key: chroma:document
string_value:
Certification Difference Between Recursive and Iterative Programs

But wait / why bother with proofs/certifications?
After all one can try fast on a few values, and see clearly that it works!
Right?

Let's have a look -> over to DrRacket. We can see in a minute that this simple program can compute some HUGE numbers - it's clearly NOT enough to say.


key: chroma:document
string_value:
Cas is all too often done
'well, it works on o ... it works on 6 => therefore it works'.

What we can gain from program proving is greatly increased confidence that our programs work.

For the fact program - there are a number of distinct factors which might make for errors
1 -> the algorithm might be wrong
2 -> the Bignum library implementing schemes in infinite precisions of integer arithmetic might be flawed


key: chroma:document
string_value:
scheme itself might be flawed

The Operating System might be flawed

The chips might be flawed

We will assume 2...5 do not occur - everything but the algorithm will be assumed to work correctly

What we'll get

testing glasses

software

proving glasses

both are necessary

neither is sufficient


key: chroma:document
string_value:
Let's take another look at the recursive version of factorial.

Start from the(math) definition of the factorial:

n! = { 1 if n = 0
      { n x (n-1)! otherwise

Assumptions: n >= 0 and n is an integer

Why isn't this a circular definition? After all it defines 'n!' in terms of ' (n-1)!' . The reason is that this is an inductive definition.

Circularity avoided by making good use of the natural metric on non-neg. integers

Here n! is defined in terms of something smaller than n -- i.e. (n-1)! 

We have no ground - or basis - case

(Always those cases for which the induction is not necessary).

The working assumption is that (n-1)! is defined.


key: chroma:document
string_value:
"first layer of unknown space" "known space" n n-1 ... : n=0 Basis (ground) or wish or induction hypothesis . IS (induction step) IH (induction hypothesis) The scheme version is just a recasting of the function definition The goal of this case is to show that Define (fact n) pre: n >= 0 ∧ n is an integer our program fact correctly computes factorial all n! ie (fact n) post: (fact n = n!) A correctness proof in this course is (cond ((zero? n) 1) (else (* n (fact (- n 1)))))


key: chroma:document
string_value:
given in two parts:
-> partial correctness
   "if the program terminates, it gives the correct answer"
-> termination argument
   "the program really does return an answer"
This is where we deploy induction -> will usually be satisfied with a hand-waving argument
Assuming that scheme works as advertized
In this case: The program starts with input n > 0. Because n is an integer, 1 can be subtracted infinitely many times before reaching 0 at which point the execution halts.
The induction argument
-> First: what are we inducting on?
   We'll induct on n


key: chroma:document
string_value:
Induction on n depends on n>=0 being an integer.

If we're doing induction, we need a well-founded set; there can be no infinite descending chains so we can't induct on
-one cannot induct on real numbers
-one cannot induct on the set of all integers

... -10 -9 -8 ... -1 0 1 2 ...

There are infinite descending chains

What is an example of an infinite descending chain of real numbers between 0 and 1?

Second, how are we decomposing the problem? Essentially: what is the IH?

For most programs, the IH -- to a first approximation -- is "we assume that the recursive call works."


key: chroma:document
string_value:
This isn't quite enough - a better approximation is
'We assume that if the precondition is satisfied, then the recursive call works'

pre: n >= 0 AND n is an integer
(define (fact n)
  (cond [(zero? n) 1]
        [else (* n (fact (- n 1)))]
  )
)
post: fact n = n!

Let's see: we know n >= 0
stat(zero? n) is false
Hence: n > 0 hence n-1 >= 0


key: chroma:document
string_value:
c we'd also need to say that n-1 is an integer, but this is immediate from the fact that n is an integer.

Third, we need to give the inductive step—i.e., we need to show that the program does the right thing with the value (n-1) returned by the call. Here (n-1)! is multiplied by n — which, according to the def of a factorial function — gives n!.


key: chroma:document
string_value:
It is important to observe what isn't done in this argument as well as to observe what is done.
Note that there is NOTHING like the pattern of calls expansion — nothing like
(fact n) calls
(fact (n-1))
calls
(fact (n-2))
...
and so on until 0
Here’s the giveaway: The ellipsis
Rule of thumb
If your argument proceeds by induction


key: chroma:document
string_value:
"unwinding" the recursion to an indefinite extent as shown by the ellipsis $	extellipsis$ then it is 	extbf{NOT} an induction. Induction - properly used - is the most powerful software engineering tool that exists. One simple argument replaces an unbounded number of unwindings... The induction argument 	extbf{MUST} make use of the induction hypothesis - the unwinding args have no induction hypothesis


key: chroma:document
string_value:
In the context of software engineering
the it amounts to a black box
which is assumed to magically
solve the smaller problem.


key: chroma:document
string_value:
Intro to Invariants - Proving Iterative Factorial

Recall
(define (fact-iter n count result)
  (cond ((= count n) result)
    (else (fact-iter n (+ count 1) (* (+ count 1) result))))
)
(define (fact n)
  (fact-iter n 0 1))

Pattern of calls — useful clue to the proof — laid out in a table with formal parameters as column headings:

n      count     result
6      0         1
6      1         1
6      2         2
6      3         6
6      4         24
6      5         120
6      6         720

Showing values each time fact-iter is called

On this call, the stopping test passes. So 720 is returned.


key: chroma:document
string_value:
The clue we seek is the pattern; at each call, 
result = count! For the first call
1 = 0! For the second
1 = 1! for the third
2 = 2!
6 = 3! etc

So we can see that this equation is true each time fact_iter is called.

We say that the equation - a relation among (some of) the program’s variables - is INVARIANT.

How does this help us prove that the program is correct?
- If the invariant holds each time fact_iter is called, then it holds the last time fact_iter is called.
- According to the code, count = n the last time fact_iter is called, and when this is true, the value of result is returned.


key: chroma:document
string_value:
-> BUT if result = count! and count = n, This means that result = n! : the correct value is returned.
 Hey - aren't you getting ahead of yourself? I mean; we did this for n = 6, not for general n.
 Of course - The expansion for n = 6 provides only a clue as to the invariant. We need to do some more work to conclude that result = count! really is invariant, no matter the input. (given that pre is satisfied!)
 So let's consider the more general situation - still for factorial, but now for an arbitrary integer n >= 0.


key: chroma:document
string_value:
An invariant is a relation among a program's variables.
Here are some 'invariant-like' creatures which are NOT what we need:

7
?
n = 6

None of these help to prove that the program worked. Specifically, we need

Is it the case that

if  A (count = n)
              =>
   result = n

?? What about

if (n = 6) ∧ (count = n) => (result = n)

?? Neither of these is a valid implication.

We will say that none of these Guess-Invariants (CGI) is STRONG ENOUGH.


key: chroma:document
string_value:
In the same vein

I != 1

(while invariant in the dictionary sense) != not an invariant we can use;

C!=1) ∧ (stopping-cond) ⇒ post-cond;

Intuitively - the problem with all of these is that they do not mention (enough of) the program's variables.

First test for a GJ: is it strong enough Heuristic guidance i.e. it needs to talk about the program's variables

(to imply the post-condition when logically ANDed with the stopping condition?)


key: chroma:document
string_value:
Important property of invariants:
Invariants are RELATIONS - They are NOT action statements. They are static assertions - in standard logic - about variables' values. They DO NOT REFER TO TIME. (No reference to "old" or "new" variable values)

We've seen that a guess/invariant can be too weak. Can it be too strong? For example, why not just take

result = n!
as the GI? Well, This relation among variables, is NOT achieved by our initialization.

We have (define (fact n)
  (fact-iter n 0 1))
and clearly it's not the case that 1 ≠ n!
in most cases


key: chroma:document
string_value:
So: second test for a GI is that it must be weak enough to be achieved (made true) by the code the first time the looping function (here fact_test) is called.

What we're working up to here is an induction proof that the invariant is true on each call of the looping function. We are setting up an induction on the number of calls to the looping function.

NOTE: that this is different from what we did for recursive factorial. In that case, the invariant was on the data (n), not on the number of calls.

amounts to the basis step.


key: chroma:document
string_value:
OUR IH: assume the GI was true for the current call. GI becomes I after it passes our tests. Our IS: show that the GI is true for the next call. We have (define (fact-iter n count result) (cond ((= count n) result) (else (fact-iter n (+ count 1) (* (+ count 1) result))))) We don’t know the area of no parameter updates - and the GI might be broken mid-stream, e.g. after this update but before the last update. The beauty is that we do NOT care about midstream: all that matters is


key: chroma:document
string_value:
Showing this is just a standard logic implication. Specifically we need that the invariant has been re-established by next call to fact. Iter. count! = result => (count+1)! = (count+1) * result and this is true by basic algebra and the def of factorial. This is the third test for a GI : is it preserved true? (i.e., that the parameter updates preserve invGT)


key: chroma:document
string_value:
Assuming that all three tests
-> strong enough?
-> weak enough?
-> preserved?
are passed, the GI is promoted to T.

we still need to give a termination
argument to complete the proof,

Much more to come! Our ultimate goal is to
use the logic during the coding process,
not merely to retrofit logic to existing
code.


key: chroma:document
string_value:
CSC 335 Class 13 Part 2 - March 14 2024

(ref. to Abelson & Sussman)
; Section 1.3.4 Procedures as Returned Values

(define (make-addConstant x)
  (lambda (y) (+ x y)))

((make-addConstant 4) 5)

(define add4 (make-addConstant 4))
(add4 3)

This is a closure
environment model simulation
evaluation of (make-addConstant 4)
needs now to be evaluated in the new-frame
This is the value resulting from evaluation of
((lambda (y) (+ x y))


key: chroma:document
string_value:
Might expect the frame x would be garbage once the evaluation is complete — but this does not happen because x is (indirectly) pointed at by an object — and A — at the top level. We say that x is live. Recall that an environment is defined to be a sequence of frames — so the addA object (or just addA) has a 2-frame environment. Let's figure out what happens when we evaluate (addA 3).


key: chroma:document
string_value:
A look at (The simulation of) (add4 3):
make-add(Constant)
add4 .
(add4 3)

As above

containing frame
is The frame
pointed at by
The closure
object, not
the frame
in which
the call
is made.

x: 4
(+ x y)
y: 3
(+ x y) giving 7

The garbage collector will
next remove the frame
in which y is bound to
3.


key: chroma:document
string_value:
; curried form of sigma (currying a function)

(define (curried-sigma-term)
  (define (sum-term a b)
    (cond ((> a b) 0)
          (else (+ (term a) (sum-term (+ a 1) b))))
  sum-term)

((curried-sigma (lambda (x) x)) 1 10)

(define sum-of-squares
  (curried-sigma (lambda (x) (* x x))))

(sum-of-squares 1 10) .

; contrast to

(sigma (lambda (x) (* x x)) 1 10)

;An uncurried sigma in which the term parameter must be passed explicitly.

Interesting for a couple of reasons - 1) it allows another level of code reuse. 2) It can facilitate efficiency by setting up partial compilation; 3) It suggests that in principle - we only need single-argument functions - function specializing.


key: chroma:document
string_value:
(define (curried-sigma term)
  (define (sum-term a b)
    (cond ((> a b) 0)
          (else (+ (term a) (sum-term (+ a 1) b))))
    sum-term)

(define-sigma (curried-sigma (lambda (x) x)))

(sigma 1 3)


key: chroma:document
string_value:
suggested exercises

- cook with curry on your own -> i.e -> Try your hand at currying a function or two

- See if you can work out the environment diagram simulation for curried-signature


key: chroma:document
string_value:
close with an example from A&S hinting at the usefulness of having functions as values.

(define (deriv f)
(let ((dx 0.00000001))
(lambda (x) ((f (+ x dx)) - (f x)) / dx)))

(define (cube x) (* x x x))

((deriv cube) 5)

The value returned is lambda(x)

(f (x + dx) - f(x) ) / dx

where dx is .0000001

so (deriv cube) gives the function of x

(cube (x + dx) - cube(x)) / dx

which you recognize as the difference quotient for cube.

Recall: deriv of cube at the point x

lim cube (x + Δx) - cube(x)
Δx->0           Δx

In calculus derivatives are functions -
and suppose a language in which
this can be expressed directly.


key: chroma:document
string_value:
CSC 335 Class 14 March 14 2024

Abbreviation for The Little Schemer - see the syllabus.
TLS - go ahead and start working through Chapters 1-6
AtS - see Chapter 2

S-expressions

An inductive definition in BNF (Backus-Naur form) for the class of S-exps is
sexp ::= atom | '(' sexp ... sexp ')'

In traditional math form, this definition might be given as follows:
The class of Sexps over {a, b} is the least class containing the atoms a and b, as well as the empty list, which is closed when the operation of forming finite lists.


key: chroma:document
string_value:
We can understand these definitions by looking at them as recipes for generating the class of s-exps over {a,b}. Remember our onions: finite lists of elements drawn from layers inside of this one. The class s-exp contains the class listT. Since these BNF definitions are a big deal going forward, possibly ((a b), ((a))) c))


key: chroma:document
string_value:
Strongly suggested: prove - by structural induction - that (*) does not generate lists with non-empty sublists.

Hint: How can the IH be applied? The key is to recognize that some list (e1, e2, ... ek) is generated by * only if e1, and e2, and ... ek are generated by *.

What about soundness? It certainly appears that every object generated by * is a list over {a, b}. But how could we prove this? It's very helpful to use a pictorial representation of lists - at this point, we will focus entirely on Scheme lists.

These pictorial reps are called box and pointer diagrams - you can find these discussed in chz of t4s.


key: chroma:document
string_value:
The basic building block of a box and pointer diagram is the cons cell. A cons cell is an assembly of two addresses (pointers).
cons cell
 car pointer
 cdr pointer
Traditional terms from the original Lisp - late 1950s 'contents address register' 'address decrement register'
A scheme list can be assembled using cons, which is a primitive. For example, the list (a b), boxed and pointed as
a -> b -> ()


key: chroma:document
string_value:
can be defined

(define l1 (cons 'a (cons 'b '())))

Aside on quotation

'a is shorthand for (quote a)

→ Quote and ' are primitives

→ Their role is to block evaluation; if you enter a (without a quote, and without having previously defined it), you’ll get an unbound variable error. But 'a just returns the symbol - a

In other words, quote gives Scheme a means for dealing with symbolic data -

When working broadly in LCS, you will need to add quote marks ahead of their symbols.

Back to cons...


key: chroma:document
string_value:
(define l1 (cons 'a (cons 'b '()))) 
If we take this apart - say 

(define l2 (cons 'b '())) 

l2 ---> [] ---> () 
            b

and then

(define l3 (cons 'a l2)) 

l3 ---> [] ---> l2 
            a              ---> [] ---> () 
                               b

which is


key: chroma:document
string_value:
Q3 -> [  ] -> [  ] -> [  ]  a     b     c  You can only have: are Q1 and Q3 "the same"?  The answer may be a bit surprising, because cons allocates new memory it is called. In fact, following the sequence of definitions just shown, we would have Q1 -> [  ] -> [  ] -> [  ] a     b     c Q2 -> [  ] -> [  ] -> [  ]            a b Q3 -> [  ] -> [  ]              a      b atoms and the empty list are unique in Scheme. There is only one '() and only one '()'


key: chroma:document
string_value:
scheme has two equally parts of particular interest to us - equal? and eq?

If you experiment with this stuff, you will find

(equal? l l3) == #t

[Same logical structure]

(eq? l l3) == #f

[Physically different addresses]

Going a bit further, you'll see that

(eq? (car l) (car l3)) == #t

how to eval? Assuming that the value of l is a cons cell

(car l) returns the value pointed at by the car pointer of that cell


key: chroma:document
string_value:
For datatypes, we will have constructors and selectors (and maybe classifiers as well). For the pair structures in Scheme, the constructor is cons and the selectors are car and cdr. Not all pair structures are lists! (will come back) We've seen that we can extract a as (car l) l-> [a]-> [t]->() a b How about extracting b? We'd get b via function composition: (cdr l) = (b) l.e -> [a]->[b]->() b


key: chroma:document
string_value:
(car (cdr l)) = 6
A scheme list - in terms of B&P diagrams - is any cons-cell structure with a "list backbone" whose rightmost cdr is ().

scheme object1 -> scheme object2 -> ... -> scheme objectk

which displays as

(so1 so2 ... soK)

On the other hand, (cons 'a 'b) is the structure


key: chroma:document
string_value:
CSC 335 Class 15 March 19 2024

Some more on cons-cell structures, let's start with an example.

Suppose we have the list (I (had (bread and (peanuts (and yoghurt)))) (for (lunch))).

How could this list be defined? How can components of the list be accessed? What is the Scheme representation of this list?

It would be error-prone to try to enter the list directly, all at once, from the keyboard. Better to do it one component at a time.

(define l4 (list 'peanuts
                 (list 'and 'yoghurt)))

Sets up in memory

l4 -> [  ] -> [  ] -> ()
      peanuts      [  ] -> ()
                      yoghurt


key: chroma:document
string_value:
Although list (the primitive constructor) is convenient, there are other ways of building QA. 
(define QA (cons 'peanuts (list (list 'and 'yoghurt)))) 
and 
(define QA (cons 'peanuts (cons (list 'and 'yoghurt) ()))) 
and 
(define QA (cons 'peanuts (cons 'and (cons 'yoghurt ()))))) 
This last form exposes the underlying structure of QA as shown above. We can use the


key: chroma:document
string_value:
B&P diagram to understand how to access components of QA. Suppose, for example, that we wish to extract the component and using the datatype selectors car and cdr. Following pointer arrows in the B&P diagram, we see (car (car (cdr QA))) = 'c' and Shorthand: (caadr QA) Recall: cons allocates a fresh cons-cell. If we had (define num1 (list 2 3)) then (define num2 (cons 1 (list 2 3))) returns num2 -> num1 -> 1 written out, this would be


key: chroma:document
string_value:
num2 → num1
1 → 2 → 3 → ())

C Reminder - no need to quote numbers, as they are self-evaluating.

This is the idea for showing soundness of list;; atom (atom ... atom)

Assuming lists are built using cons and recalling that lists are built from flat cars-cell backbones with rightmost cdr

The empty list - you can see that consing an atom to a list of atoms again returns a list.

Back to Q4 - to type it in directly, one could use quote 
(Define Q4 '(Peanuts (and Yoghourt)))


key: chroma:document
string_value:
Note, however, that

(define RL '( (peanuts (list and yoghurt)) ) )
returns

(peanuts (list and yoghurt))

unevaluated due to the quote.

Exercise: Figure out additional components of
RL and then how to define RL (Z)
Draw RL and access various components
of RL.


key: chroma:document
string_value:
Another example: if R1 and R2 are lists, what is the value of (cons R1 R2)?
E.g.: R1 = '(1 2)
       R2 = '(3 4)
       (cons R1 R2) = ((1 2) 3 4)

There is another primitive list op called append:
       (append R1 R2) = (1 2 3 4)


key: chroma:document
string_value:
Let's look at some program developments using lists.
Consider a program which is to input a list and output #t if the input is a list of atoms, and #f otherwise.

Here we will assume

lst::= () (cons atom list) (cons list list)

and

lat ::= () (cons atom lat)
down::= a|b

The first definition describes the input data, and effectively tells us how to structure the program

(define (lat q)
  (cond ((null? q) #t)
        ((atom? (car q)) ________?? )
        (else #f))))

          (recursive call on a shorter list - i.e (lat (cdr q)))


key: chroma:document
string_value:
check out the above to avoid having to type def. of atom? each time you want it. atom? is NOT a primitive - so we define it (define (atom? x) (and (not (null? x)) (not (pair? x)))) Here null? and pair? are primitives (null?. ?) = #t if l is the empty list #f otherwise (What does scheme do if l is not a list?) (pair? . ) = #t if c is a cons-cell #f otherwise (What does scheme do if c is say a number?)


key: chroma:document
string_value:
(define (lat? s)
(cond ((null? s) #t)
      ((atom? (car s))     ?)
      (else #f)))
Recursive call on a shorter list -
(lat (cdr s))
Why that else-clause? How about
(define (lat? s)
(cond ((null? s) #t)
      (else (and (atom? (car s)) (lat? (cdr s)))))
Cleaner. Simpler. More in line with the BNF.
Pf by induction on the length of the input #elts in the list
Strategy used: 'cdring down the list' -


key: chroma:document
string_value:
With this design idea, plus the idea of processing the tail of the list recursively we have our divide and conquer set up. i.e., solved by 'recursive magic' processed by checking whether it is an atom. We know that the list is a list precisely if BOTH the car is an atom and the cdr is a list. So we use 'and', which is the deferred operator.


key: chroma:document
string_value:
Some Problems from HW5 — concerning higher order functions.

A&S 1.29 (Implementing Simpson's Rule)

The text states, "Using Simpson's Rule the integral of a function f between a and b is approximated as

\(\frac{h}{3} [y_0 + 4y_1 + 2y_2 + 4y_3 + 2y_4 + ... + 2y_{n-2} + 4y_{n-1} + y_n]\)

where h = \(\frac{(b-a)}{n}\) for some even integer n, and y_k = f(a+k*h)"


key: chroma:document
string_value:
we are asked to code this.
;assumes that n is even
(define (simpson f a b n)
  (simp-aux f a b n (/ (- b a) (* n 1.0))))

(define (simp-aux f a b h)
  (define (term k)
    (cond ((= k 0) (* h f a))
          ((= n k) (* h (* a (* k h))))
          ((odd? k) (* h (* 4 (f (+ a (* k h))))))
          ((even? k) (* h (* 2 (f (+ a (* k h))))))
    ))
  (define (next k)
    (+ k 1))
  (* (/ h 3.0)
     (sum-term 0 next n)))

Simpson's Rule uses parabolas instead of rectangles to integrate the integral approximation. R is an integer (want to avoid floating point issues).

Note that I am not using the yk values to control the h op.

What is to be proved?

Clearly we would NOT want to give a pt of Simpson's Rule itself - Rather - the proof burden would be to show that this code actually does IMPLEMENT Simpson's Rule.

A useful verification would be showing diagrammatically that the code does what Simpson's Rule requires.

As for real-number pitfalls - I'd like you to write a loop with stopping condition k = 1, 'n' x initially 0 which adds 0.01 to x with each iteration x:= 0.0
While < x 1.0 do x:= x + 0.01 It does not stop!


key: chroma:document
string_value:
There is an easy way to avoid the repeated multiplication (x * k * k) - think about introducing a new variable to keep track of the right endpoints.

Note that Problem 1.30 - negative sigma - has been discussed in class

Problem 1.31 - Another example of the use of a specialized sigma term parameter in the computation of Wallis' product:

π = 2 * (2/1) * (2/3) * (4/3) * (4/5) * (6/5) * (6/7) * 8 * ... 
Δ = 3 * (3/1) * (3/3) * (5/3) * (5/5) * 7 * 7 * ...

(define (wallis n)
  (define (term k)
    (cond ((odd? k) (* 1.0 (/ (+ k 1) (+ k 2))))
          (else (* 1.0 (/ (+ k 2) (+ k 1))))))
  (define (next_k) (+ k 1))
  (iter-prod term 1 next n))

Where you can fill in the details for the function iter-prod.


key: chroma:document
string_value:
Let's take a break from the homework problems with an Iterative Count Change program. A recursive divide and conquer approach is developed in the text (pp 40-41): The number of ways to change amount a using n kinds of coins equals the number of ways to change amount a using all but the first kind of coin, plus the number of ways to change amount a - d using n kinds of coins, where d is the denomination of the first kind of coin. What might an iterative approach look like? One approach essentially solves the problem via an iterative solver.


key: chroma:document
string_value:
;; the only looping here is in sigma

;; GI: result = sum of term(i) from A to a-1 AND a <= b + 1

(define (sigma a term b)
    (define (iter a result)
        (cond ((> a b) result)
              (else (iter (+ a 1) (+ (term a) result)))))
    (iter a 0))

;; the max number of nickels is {quotient amt 5}, and for each 0 <= i <={quotient amt 5}, there is
;; way of making change using i nickels (with pennies as the balance)

(define (pn amt)
    (cond ((< amt 5) 1)
          (else (+ 1 {quotient amt 5}))))

;; the max number of dimes is {quotient amt 10}, and for each 0 <= i <= {quotient amt 10}, there
;; (pn (- amt (* 10 i))) ways of making change

(define (pnd amt)
    (sigma 0
           (lambda (numberOfDimes) (pn (- amt (* 10 numberOfDimes))))
           (quotient amt 10)))

(define (pndq amt)
    (sigma 0
           (lambda (numberOfQuarters) (pnd (- amt (* 25 numberOfQuarters))))
           (quotient amt 25)))

(define (pndqh amt)
    (sigma 0
           (lambda (numberOfHalves) (pndq (- amt (* 50 numberOfHalves))))
           (quotient amt 50)))

No new invariant needed since
we've already proved that sigma
works.


key: chroma:document
string_value:
Suggested for you

These last functions are so small that our inner functional programmer wants to ask: can they be abstracted as we did earlier for sigma?

Well, back to work ...


key: chroma:document
string_value:
Problem 1.34

(define (f g)
  (g 2))

What happens when we call

(f f)?

This results in (f 2),
which gives (2 2), which
throws an error because (2 2)
makes no sense as a function
call.


key: chroma:document
string_value:
(Continued Fractions)
Exercise 1.37

; iterative_version
(define (cont-frac n d k)

(define (cont-iter result count)
(if (= count 0)
result
(cont-iter
(/ (+ (n count) (d count) result))
(- count 1)))))

(cont-iter (/ (n k) (d k))
(- k 1)))

; test with golden ratio
(cont-frac
(lambda (i) 1.0)
(lambda (i) 1.0)
1000000)

; guess-invariant: result =

;
;
;
;
;
;
;
;
;

; so now when count = 0, the first numerator included in result is
; N(1), or is it N(0) ?

Numbers a and b with a > b are said to be in the golden ratio if their
ratio is the same as the ratio of their sum to a. That is, if a/b = (a + b)/b
Approx 1.6180339 ...


key: chroma:document
string_value:
What is the next value of result?
N_k-1          (R)         t
________ = _______  + _____
D_k-1 + result   t+1

And the next value?
N_k-2          (R)         t
________ = _______  + _____
D_k-2 + result   t+2

And so on – we can see the emerging pattern, even if we might not see how to describe this pattern with anything other than a t-fort

Suggested for you: If the terms above are the 0th, 1st and 2nd terms, what does the nth term look like?


key: chroma:document
string_value:
Does Nk-p
Dk-p + (N
k-(p-1))
Dk-(p-1) +
Perhaps this could be abbreviated as cont-frac(N,D,p-1)?
So that as gi we mayyt have
result = Nk-p
Dk-p + cont-frac(N,D,p-1)
? But we've lost the variable count. Can you complete this by letting expressing p in terms of count?
I'll leave the recursive solution to you. (It's easier.)


key: chroma:document
string_value:
Moving right along:

;; exercise 1.42
(define (compose f g)
  (lambda (x) (f (g x))))

(define (addone x)
  (+ x 1))

(define (square x)
  (* x x))

((compose square addone) 3)

; what is assumed about f and g?

(square (addone 3)) = 36

Could we have (lambda(x) ...) as the value of a call to compose? Yes!

; could take multiple args, with a singleton output. E.g.: g could be 2-arg and the function rewritten

; as far as we have gone, our functions all produce a 1-dimensional output

E.g.: we have no way now of directly writing a function f: Z^2 -> Z^2

; of course, we could be clever and find a way to encode pairs of integers as single integers. (E.g. Cantor's diagonal proof that set of pairs of integers is countable (Z^2...))


key: chroma:document
string_value:
define new-compose f g
(lambda (x y) (f (g x y))))

What is the diagram for compose.

compose:

                ____________
              Params (f g)


x, y need to satisfy precond for g and (g x y) needs to satisfy precond for f

body (lambda (x y) (f (g x y)))

Practice: work out diagram for
(compose sg myadd) 2 3

The proof for compose itself has nothing to do with the proofs of f and g


key: chroma:document
string_value:
Is there truly no way of writing a function (with just what we know now) capable of returning multiple values? It is true that we can get only a single value now, but we could use that value to encode (e.g., pairs, triples, etc.) One way if we want outputs of 3 values (p, q, and r), have the function compute 2^p * 3^q * 5^r - extracting p, q, r is then just a matter of (what amounts to) taking base 2, base 3, base 5 logs. 'Fancy way of saying: we can do repeated division by 2, to extract p (and so on)'


key: chroma:document
string_value:
There are other ways to see the argument that the set of pairs (p, q) of integers has the same size as the set of integers itself. Here's the idea: The analytic form of the function tracing this traversal curve (and that for its inverse) are known.


key: chroma:document
string_value:
exercise 1.43

(define (repeated f k)
  (if (= k 1)
      f
      (compose f (repeated f (- k 1)))))

(define (compose f g)
  (lambda (x) (f (g x))))

(define (square x)
  (* x x))

((repeated square 2) 5)

; iterative solution:

; design idea - keep a variable result-so-far and another variable count - and maintain result-so-far = f composed with itself count times

Note: looks like count would most usefully increase from 0 up to count - stop when count = k, then result-so-far would be f^k.

((repeated sq 2) 3) = (sq (sq 3)) = (sq 9) = 81

((repeated sq 3) 2) - (sq (sq (sq 2))) = (sq 4) = 16


key: chroma:document
string_value:
i > 0 is an integer 
define (crepeated f k) 
  (iter 0 k f (lambda (x) x)) 

(define (iter count k f result-so-far) 
  ...) 

i > 1 is an integer 
define (npeated f k) 
  (iter 1 k f f) 

(define (iter count k f result-so-far) 
  (cond ((= count k) result-so-far) 
    (else 
      (iter (+ count 1) k f (compose f result-so-far))))) 

Aside: 
  (lambda (x) (f x)) 
  is the same as 
  f. 

We want to check the invariant, but this thing 
was built with a specific girl in mind! 

Suggested practice: work out the down-converting version, in which ef no 
longer need to be passed as parameter.


key: chroma:document
string_value:
Class 07 CSC 335 Sections M and R 02/15/24
Early Examples of Program Development

Computing b^n where, for simplicity, we shall assume that n >= 0 is an integer, and b is just a number.

Specifically - just b * b x ... x b

n times

The first question: should we use recursion, or iteration? Generally it is much easier to use recursion - so we start with that. Having decided that, the next question is: how will we 'divide and conquer'? Specifically what related (but smaller) problem will we solve, and how will we use that solution to solve the larger (main) problem?

b x ... x b
n-1 times

Note that the first step in a is NOT 'what is the basis step?' The reason is clear - we can't know what the basis step is until we know what we are trying to prove.

But at this point - with the divide and conquer strategy...


key: chroma:document
string_value:
In place we can ask: what problem of this kind cannot be subdivided? And that is the basis step for the strategy we propose, the n=0 case cannot be subdivided—so we must compute it directly (without recursion). 

Please notice that the strategy does not further decompose—specifically, it does NOT spell out '... and then we decompose the b^(n-1) problem, and then the b^(n-2) problem (...) and so on until the b^0 problem is reached', 

The divide and conquer strategy has some serious magic; the divide step includes a black-box component. We do NOT ask how b^(n-1) is computed—we simply ASSUME that it is. This relies on the Principle of Induction. 

Let's take a moment to understand the difference between the EVIL and LAZY ellipses. 

Here it is: the lazy ellipsis can always be replaced. For example, 


key: chroma:document
string_value:
∀x...×b 
 n-1 times 
 n-1 
 can be replaced by ∏ (from i=1 to n-1) b. 
 Notated aside: Hey, wait: i doesn't occur in the term 
 So what does this mean? The idea is to regard ∏ (from i=1 to n-1) as ∏ (from i=1 to n-1) f(i), where f(i) is the constant b for all values of i. 
 In other words, we wrote b×...×b, but we did not need to. 
 For the EVIL ellipsis, there is no way of replacing it. One way or another, a description at that level involves the concept ''eventually''. You need to carry out an induction.


key: chroma:document
string_value:
Another point, having to do with workflow: design in English, not in code.

Supporting argument: you will find that the human-language descriptions are more abstract than the computer-language designs.

By this I mean that usually there are many possible computer encodings of a given algorithm.

Algorithmic Idea

<multiple arrows pointing to different objects including circled 'Algorithmic Idea'>

<sketch of three heads and a circle>

many possible programs

Each program makes many specific


key: chroma:document
string_value:
e.g. choices regarding data representation, precisely what to do next and so on. As far as solving the org. problem is concerned, they are over-constrained.

Related: programming languages and errors for designing solutions. So you don't want to use them for this.

Let's get back to b. At this point we might generate some guess-code

pre: b is a number and n >= 0 is an integer

define (myexp b n)
    (if (zero? n) 1
        (else (* b (myexp b (- n 1))))

pos: returns b


key: chroma:document
string_value:
IH: The recursive call works, provided the pre-cond holds at the point of the call.

We have to show - in this particular case - that PRE ∧ n ≠ 0 => b is a number and n-1 > 0 

This is clear: n ≥ 0 ∧ n ≠ 0 => n-1 ≥ 0 if b was a number, then - because the program never changes b - it is still a number.

So (myexp) b (- n-1) actually does return b, the program multiplies this by b, hence giving the correct result.

So - are we done? Nope - still have to check the basis step.

We have to show that


key: chroma:document
string_value:
PRE A (n=0) -> 
1 is the right answer
But this is not a valid implication, because our current PRE allows b=0; In case 
n=0, the result 0^0 is undefined - 
The program should certainly not return 1.
So; what to do?
No input checking in 335
No error message outputs in this course.
We can change the precondition - one 
possibility is
b ≠ 0 is a number and n≥0 is an integer
Another - less restrictive, and so superior if 
it works - is
b is a number and n≥0 is an integer
and (b and n are not both 0)


key: chroma:document
string_value:
P1 solves our problem - for now
b^0 ^ n = 0 => b = 1
(This is the basis step)

You can check that the induction step
is not bothered by this new precondition
because... the new precondition
implies the old precondition.

but it would be nice if we could use
P2. One way might be

; P2
(define (another-expt b n)
  (cond ((zero? b) 0)
    (if b ≠ 0 (myexpt b n) computes b^n)
    (else (myexpt b n))))
;b^n is returned.


key: chroma:document
string_value:
Homework: you might try using P2 with the original program (i.e., no checking whether b = 0) – and giving a 'no-unwinding' analysis of what goes wrong.

The unwinding analysis might go as follows: if b = 0, then since eventually n = 0, we will be confronted with 0 – so the whole plan fails.

Hint: The fault rests in an invalid implication.

The termination argument – which is essentially the same as for recursive factorial – needs yet to be given.


key: chroma:document
string_value:
Grading Rubric for a problem of this kind:

1. Specification - i.e. pre and post

2. Decide whether to use recursion or iteration - usually start with recursion because it's easier

2.1 Having decided on recursion
   -> functional
   -> decomposition if more than one function seems to be necessary
   -> divide & conquer strategy (D & C)
   -> IH: what will we induct on?
   -> either has base or the basis
     -> base step is given in terms of IH & D & C
     -> for these types of problems,
       number problem,
       it is usually some basic
       algebra
   -> check that the pre-condition holds
      when the recursive call is made
   -> check that pre & basis ->
      post condition
   -> Termination arg

if any of these steps break,
reverse possibly
starting over


key: chroma:document
string_value:
2. Invariant first / code later development of an iterative exponentiation program.

We start with a design idea: we wish to compute b^n (b + 0, n = 0 - b is a number and n is an integer) in the following way: using an accumulator variable result, we will increase a counter - count - from 0 up to n, with b^count kept in result.

When we stop, count = n, and result should be b^n. We have a guess-invariant:

result = b^count

-> Is this strong enough? [Yes; when count = n we can return result]

-> Is it weak enough? [Yes - if we set result initially to 1, we have result = b when count = 0]

-> Is it preservable? [Yes: if result = b^count for the current call, is this still true on the next call? Our design idea was to increase count - presumably by 1 - so


key: chroma:document
string_value:
all we need do to maintain the equation is to multiply result by b.

Does the proposed design terminate? Yes - incrementing count from 0 to integer n >= 0 will terminate.

Generic Design Idea
"Make progress towards termination while keeping the invariant true"

Guess-code
(define (my-expt b n)
   (some call to expt-iter )
(define (expt-iter b n count result)
   (cond ((= count n) result)
         (else (expt-iter b n (+ count 1) (* b result)))))


key: chroma:document
string_value:
let's test... need first to set up the initial call to expt-iter
The initial call is supposed to make the invariant true
the first time expt-iter is called
(defun (myexpt b n)
(expt-iter b n 0 1))

Is there an alternate design? As engineers, we need to do better than settle for the first idea that occurs to us,

Try to look at your designs critically

For the program we just wrote, one criticism might be that we need at each step to compare count to n


key: chroma:document
string_value:
If we were to start count at n, and decrease it to 0, would our current invariant still work?

If we were to start count at n, and decrease it to 0, would we still need all the variables?

Let's take the first question: currently our GI is 
result = count

Does anything go wrong when we change the design?

One problem: on start, count = n, 
in so result would need to be bn. And this means that


key: chroma:document
string_value:
The problem has already been solved - there is no reason to run the program!
Another problem: It's also not strong enough - in that it does not imply the postcondition when n = 0 (i.e., when the proposed loop stops).
Yet it seems that we ought to be able to compute n by counting down to 0 - so perhaps the thing to do


key: chroma:document
string_value:
is to adjust our GI!

result = b ^ count ?

looks better.

so the exponent
is 0 when
count = 0 and

HW
Suggested for you:
Work this out - ie - modify the
previously given
code so that
this GI works


key: chroma:document
string_value:
Class 08 Sections M and R February 20 and 27 2024

We wrapped up last time by sketching the design of a downcounting exponentiation program, thinking that such a design might allow us to eliminate a parameter from expt-iter: if we start count at n and finish when count = 0, we no longer need the parameter n in expt-iter.

Recalling that a 'design idea', as we use the term in 335, consists in large part of a GI, we start today by giving a second development of a GI for this program.


key: chroma:document
string_value:
Part of the design idea - what about a GUI? We need a certain kind of platform which makes a connection between the program variables and the desired end result. Here - between count, result and b.

When the program stops, we want result = b^n

When the program starts, we have count=0 and we need to have a value for result which is 'easily' computed.


key: chroma:document
string_value:
Suppose result = 1 to start. Then b = ?? * result
So if b = count * result is weak enough
If we make this b then on start we would have b = count^n * result
Moreover, when count = 0 this equation would give us b^n = b^0 * result ie, result = b^n.
The only thing left to check is whether it is presentable? The plan is to decrement count - and so we need to adjust result to keep the equation true. Clearly, if


key: chroma:document
string_value:
b^n = b^(count) x result
then also
b^n = b^(count-1) x (b x result)
So: we just need to multiply result by b

So we have guess-code for an alternate design:

(define (expt-iter b count result)
  (cond ((zero? count) result)
    (else (expt-iter b (- count 1)
      (* b result)))))

Note: the n parameter has been removed.

With this design
there is no need to compare b and n.


key: chroma:document
string_value:
2.
Let's look now at an iterative program for adding two numbers a and b.

We'll assume a>=0 is an integer but you will certainly want to see about removing a>=0 restriction.

Design idea: addition by repeated incrementation. We will start with a and b and simultaneously decrement a and increment b until a=0.

Since we decrement a at the same time as we increment b, a likely GI is:

a + b is constant

How does this help? - If we had a convenient way of referring to the initial values of a and b...


key: chroma:document
string_value:
then say as A and B, respectively, then we could write ghost variables never change A+B = a+b why -> strong enough? On termination, our plan is that a = 0. So the eqn then implies A+B = 0 + b = b and we could simply return b to get the desired sum. -> weak enough? On start, a = A and b = B. So certainly A+B = a+b


key: chroma:document
string_value:
-> preservable?
A+B = a+b

A+B = (a-1) + (b+1)

So - bang! - we have code
define (myplus a b)
  (cond ((zero? a) b)
    (else (myplus (- a 1)
                   (+ b 1))))


key: chroma:document
string_value:
What about the recursive procedure for the same task, given my myplus-version-1 in A & S?

How could we use a divide and conquer strategy to solve the problem?

How will we break the problem up?
a + b = 1 + ((a-1) + b) -> 'smaller problem of the same kind'

adjustment necessary to leverage the result from the smaller problem to solve the entire problem

Termination when a = 0

Tentative Basis: (0 + b) = b


key: chroma:document
string_value:
It appears we can organize the construction and proof as an induction on a

Reality check: is <something> which can be instructed on.

Well yea - a >= 0 is an integer.

So we're ready for guess code - 
(define (myadd a b)
  (cond ((zero? a) b)
    (else (+ 1 (myadd (- a 1) b))))

The TS: we add 1 to (a - 1) + b provided: the proposition holds
at the time of the call


key: chroma:document
string_value:
Classes 10 and 11 CSc 335 February 29 and March 5 2024

Exercise 1.12 from A & S – The Pascal's Triangle Problem

using 0-based indexing, this is the set of Pascal’s triangle which, This gives input to want the value (3,2) writing this actual index, refers to this position.

We are to write a program which inputs the "index" of an entry in this table and which returns the corresponding value.

What is the definition of index in this case? After some thought, we decide to use row-column indexing where columns are diagonal. Given this, there is still the question of whether to use 0-based or 1-based indices.

For example - should the leftmost column be the 0th column or the 1st column?

Since we can in all likelihood develop a program for either choice, we ask whether there is another criterion we can use to make the decision.

As you probably know, the numbers in the Triangle are precisely the binomial coefficients - ie - the numbers (n k) which occur as the coefficients.


key: chroma:document
string_value:
In the expansion of (x+y)^n. So it makes sense that the convention we use lines up with this.

Let's compute some values for (n k).

(0 0) = 1

(1 0) = 1
(1 1) = 1

(2 0) = 1
(2 1) = 2
(2 2) = 1

Recall (n k) =
n! / [k! (n-k)!]

(0 0) (1 0) (1 1)

(2 0) (2 1) (2 2)

(3 0) (3 1) (3 2) (3 3)

Suggest checking out 1-based indexing and work out the program.

Looks like we are best served by using 0-based indexing for both the rows and the columns.

With this in mind, we can tighten the program's specificity.


key: chroma:document
string_value:
pre: row and col are integers such that
(row col) makes sense as a binomial coefficient

pre - bcd are non-negative integers and
col <= row

program
post: returns (col)

Start by understanding the recursion
-> if col = 0 return 1
-> if col = row, return 1
-> Otherwise
(pascal row col) is computed from
(pascal (row-1) (col-1))
and
(pascal (row-1) col)
col) is not reduced, so can't induct on col


key: chroma:document
string_value:
Tentative HH: The recursive calls work so long as the precondition is satisfied AND the col index is no more than row-1. 
We know col <= row and we know neither row nor col is 0 and we know col < row so clearly col-1 <= row-1 and also col <= row-1 because everything is an int[eger]. 
Termination is clean because for each recursive call, the row index is reduced by 1 -- and the program halts when row = 0. 

Is our design ready to be tested as code? 
-> Design roles for variables 
-> Divide and conquer strategy - i.e. the shape of the HH and its - is in place 
-> Termination argument is in place


key: chroma:document
string_value:
Looks like we're ready to code!

(define (pos row col)
  (cond ((= row 0) 1)
        ((or (= col 0) (= col row)) 1)
        (else (+ (pos (- row 1) (- col 1))
               (pos (- row 1) col)))))

Next, check for consistency with the development

eg - does this work and well?

(define (pul row col)
  (cmd ((= col 0) 1)
       ((= col row) 1)
       (else ... as above ...)))

Next: run a few tests

Next: (if time) write out a concise proof, which effectively summarizes your development.


key: chroma:document
string_value:
That's a wrap!

In the typed note, you'll find a version which makes a different choice for the indexing.

What about an iterative solution?

- could use iterative factorial in the formula given above, but considerable care needs to be taken to avoid unnecessary duplication of effort.

- another way would be to make use of lists or vectors with the idea of computing the nth row (represented as a list, say) from the (n-1)st row. Will come back.

This brings into question what is meant by the phrase


key: chroma:document
string_value:
"Iterative programs work in constant space" because - clearly - these rows grow longer and longer as the computation proceeds. Similarly, the space needed for (fact (- n 1)) increases as n increases. What we mean is, "the stack of calls does not grow"


key: chroma:document
string_value:
Exercise 1.11 - as given in the text
; a function f is defined by the rule that f(n) = n if n < 3 and 
; f(n) = f(n-1) + 2*f(n-2) + 3*f(n-3) if n >= 3. Write a procedure that 
; computes f by means of a recursive process. Write a procedure that 
; computes f by means of an iterative process.

Here is a procedure that computes f by means of a recursive process

(define (f n)
  (cond ((< n 3) n)
        (else (+ (f (+ n -1)) (+ 2 (f (+ n -2))) (+ 3 (f (+ n -3)))))))

; where the updates, just as parameter updates in scheme, do not interfere with one another

(define (f-iterative n)
  (define (f-iter m w x y z)
    (cond ((= m n) w)
          (else (f-iter (+ m 1) (+ w (+ x (* 2 y) (+* 3 z))) w x y))))
  (f-iter 3 2 (+ 2 1) (+ 3 0) 1 0))


