SML - Get Indices of List

三世轮回 提交于 2019-12-24 02:09:54

问题


I'm working on a program that appends either a '+' or '-' to an element of a list, depending on whether the index of that element is odd or even (i.e an alternating sums list).

However, I'm having trouble identifying what the index of each element is. I have code that I believe should append the correct symbol, using if statements and mod

fun alternating([]) = 0
  | alternating(l) = 
        if List.nth(l,hd(l)) mod 2 == 0 then '+'@hd(l)@alternating(tl(l))
        else '-'@hd(l)@alternating(tl(l))

However, List.nth(l,hd(l)) always returns the element at the second index, not the first.


回答1:


On the off chance that you really just want to negate integers them so you can pass them into some kind of summation, I would just negate the argument if it's odd. Using mutual recursion one can do it without any explicit index bookkeeping:

fun alternate l =
   let
       fun alternate1 []      = []
         | alternate1 (x::xs) = (~x) :: alternate2 xs
       and alternate2 []      = []
         | alternate2 (x::xs) =   x  :: alternate1 xs
   in
       alternate1 l
   end

It works like so:

- alternate [1,2,3,4];
val it = [~1,2,~3,4] : int list

I would strongly encourage you to use pattern matching instead of hd.

Edit discussing hd

As a rule of thumb, if you need hd you probably need tl as well. hd is a partial function--it's going to throw Empty if your list is empty. If you pattern match, you conveniently get variables for the head and tail of the list right there, and you get a visual reminder that you need to handle the empty list. It's more aesthetically pleasing, IMO, to see:

fun foo []      = ...
  | foo (x::xs) = ...

than the equivalent

fun foo l = 
  if null l 
    then ... 
    else (hd l) ... (tl l)

In other words, you get shorter, cleaner code with an automatic reminder to make it correct. Win/win. To my knowledge there's no significant advantage to doing it the other way. Of course, you may find yourself in a situation where you know the list will have at least one element and you don't need to do anything else. You still have to consider the cases you're given, but it's a good rule of thumb.




回答2:


If you want to decorate your list with an index you could try something like the following

fun add_index l =
  let 
    fun add_index_helper (nil,  _) = nil
      | add_index_helper (h::tl,i) = (h,i) :: add_index_helper (tl,1+i)
  in
     add_index_helper (l,0)
  end

val x = add_index [0,1,4,9,16,25]

but you can also just directly compute parity with the same method

fun add_sign l =
  let 
    fun add_sign_helper (nil,  _) = nil
      | add_sign_helper (h::tl,i) = (h,i) :: add_sign_helper (tl,1-i)
  in
     add_sign_helper (l,0)
  end

val y = add_sign [0,1,4,9,16,25]

then you can map the parity to a string

fun sign_to_char (x,0) = (x,"+")
  | sign_to_char (x,_) = (x,"-")

val z = List.map sign_to_char y

or you can just add the sign directly

fun add_char l =
  let 
    fun add_char_helper (nil,  _) = nil
      | add_char_helper (h::tl,0) = (h,"+") :: add_char_helper (tl,1)
      | add_char_helper (h::tl,_) = (h,"-") :: add_char_helper (tl,0)
  in
     add_char_helper (l,0)
  end

val zz = add_char [0,1,4,9,16,25]

Alternatively if you had a string list and you wanted to add chars you could try something like this

fun signs L =
  let
    datatype parity = even | odd
    fun signs_helper ( nil ,_) = nil
      | signs_helper (x::xs,even) = ("+" ^ x) :: signs_helper(xs,odd)
      | signs_helper (x::xs,odd)  = ("-" ^ x) :: signs_helper(xs,even)
  in
    signs_helper (L,even)
  end

val z = signs ["x","2y","3z","4"]
(* this gives you val z = ["+x","-2y","+3z","-4"] : string list *)


来源:https://stackoverflow.com/questions/26660505/sml-get-indices-of-list

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!