3
votes

I am new in sml. I tried to convert int to int list. For example, assume that there is an input 1234, then output is a list like [1,2,3,4]. And my question is, how can I type nested functions in sml? let in end? There is my code.

 fun digit (a : int): int =

    let
            fun size (a) = if a < 0 then nil
                    else x = Int.toString x then digit s = size(x)

            fun insert (num, nil) = [num]
                    |       insert (num,xs) = x :: insert ()

            fun convert (a, s) = if s < 0 then nil
                            else insert (a / (10*(s - 1)), xs)
                                    then convert(a - (10*(s - 1), s - 1)

    in

    end
2
For your second question: yes. You will need let in end. Some resources cs.cornell.edu/courses/cs312/2006fa/schedule.htmTai
Do you mean how to use the functions like insert and convert you defined in "let in end"?Tai
Yes, and I referred to link you gave. I found that in Rec.5 but I can't understand what should I type on "in", I think there may leave out "in" line and execute the digit function, right?kw Hyun
You should try to make use of the functions you defined previously between in and 'end. I wrote something. Hope that is helpful.Tai
I think it would be a good idea to focus on writing correct non-nested functions first – those are nonsense.molbdnilo

2 Answers

2
votes

Nested functions are just one way to split up your workload in multiple, smaller parts. Another option is non-nested library functions. The main differences are that functions that aren't nested don't inherit its parent's variable scope, so they can only work with their own input, and functions that are nested aren't available anywhere else and can't be re-used. Let's say you're giving this problem a first stab:

fun digit_meh n = if n < 10 then [n] else n mod 10 :: digit_meh (n div 10)

And you realize it isn't doing exactly as you want:

- digit_meh 1234;
> val it = [4, 3, 2, 1] : int list
  • You could remove the most significant digit first, but the calculation isn't as trivial as n mod 10, since it depends on the number of digits.

  • You could generate this list and then reverse it:

    fun digit n = rev (digit_meh n)
    

    But the function digit_meh isn't particularly useful outside of this function, so it could be hidden using local-in-end or let-in-end:

    local
      fun digit_meh n = if n < 10 then [n] else n mod 10 :: digit_meh (n div 10)
    in
      val digit = rev o digit_meh
    end
    
    fun digit n =
        let fun meh n = if n < 10 then [n] else n mod 10 :: meh (n div 10)
        in rev (meh n) end
    

    Do notice that the function meh's copy of n shadows digit's copy of n.

    For clarity you could also name the variables differently.

  • Or you could look at how rev is doing its thing and do that. It basically treats its input as a stack and puts the top element in a new stack recursively so that the top becomes the bottom, much like StackOverflow's logo would look like if it jumped out and landed upside down like a slinky spring:

    fun rev L =
        let fun rev_stack [] result = result
              | rev_stack (x::xs) result = rev_stack xs (x::result)
        in rev_stack L [] end
    

    Because the result is accumulated in an additional argument, and rev should only take a single argument, nesting a function with an extra accumulating argument is a really useful trick.

    You can mimic this behavior, too:

    fun digit N =
        let fun digit_stack n result =
                if n < 10
                then n::result
                else digit_stack (n div 10) (n mod 10::result)
        in f N [] end
    

    This way, we continue to treat the least significant digit first, but we put it in the stack result which means it ends up at the bottom / end. So we don't need to call rev and save that iteration of the list.

In practice, you don't have to hide helper functions using either local-in-end or let-in-end; while it can be useful in the case of let-in-end to inherit a parent function's scope, it is not necessary to hide your functions once you start using modules with opaque signatures (the :> operator):

signature DIGIT =
sig
  val digit : int -> int list
end

structure Digit :> DIGIT =
struct
  fun digit_stack n result =
      if n < 10
      then n::result
      else digit_stack (n div 10) (n mod 10::result)

  fun digit n = digit_stack n []
end

As this is entered into a REPL, only the relevant function is available outside of the module:

> structure Digit : {val digit : int -> int list}
  signature DIGIT = {val digit : int -> int list}
- Digit.digit 1234;
> val it = [1, 2, 3, 4] : int list
2
votes
fun aFunctionCallingF2F3 someVariables =
   let
       <define some functions and local variables here>
       fun F2 ...
       fun F3 ...
       val v1 ...
       val v2 ...
   in
       <Make use of the functions/variables you defined above and `someVariables`>
   end

For example,

fun areaCirle r:real = 
   let fun square x:real = x*x
       val pi = 3.14
   in 
      pi * square r
   end

Or define functions you need to call beforehand if they are not mutually recursive. If they are mutually recursive, you can look up the keyword and.

fun F2 ...
fun F3 ...
fun aFunctionCallingF2F3 = <make use of F2 F3 directly>

For example,

fun square x:real = x * x
fun areaCircle r = square r * 3.14

Note that you cannot do

fun areaCircle r = square r * 3.14
fun square x:real = x * x

square needs to be defined before areaCircle.