0
votes

I'm new to Scala and to Functional Programming. I'm busy rewriting a Python (procedural) module into a Scala Object Class. I'm stuck where I need to define a method (ExtractTime) that takes in a String and Map (Dictionary) as arguments and returns the result as a tuple based the val functions results within the method. I have three functions within the method that return "Hours", "Minutes", "Period". The error message that I've received is that the return type is "Unit" and the required output is (Int, Int, String). How can I return the values within the "ExtractTime" method as a tuple, note that the output types are not the same (i.e. Int, Int, String).

package Time

/**
  * Created by PeterW on 6/11/2017.
  */
object TimeAsWords2 extends App {

  // Input string argument is based on 24hr format 23:59
  val InputTime = args(0)

  // regular expression string
  val Exp24hr = """^(([01]\d|2[0-3]):([0-5]\d)|23:59)$"""

  // a Map of word equivalent of integer values
  val WordsLookup = Map(1 -> "one", 2 -> "two", 3 -> "three", 4 -> "four",
    5 -> "five", 6 -> "six", 7 -> "seven", 8 -> "eight",
    9 -> "nine", 10 -> "ten", 11 -> "eleven", 12 -> "twelve",
    13 -> "thirteen", 14 -> "fourteen", 16 -> "sixteen",
    17 -> "seventeen", 18 -> "eighteen", 19 -> "nineteen",
    20 -> "twenty", 21 -> "twenty one", 22 -> "twenty two",
    23 -> "twenty three", 24 -> "twenty four", 25 -> "twenty five",
    26 -> "twenty six", 27 -> "twenty seven", 28 -> "twenty eight",
    29 -> "twenty nine")

  // a Map of time periods
  val PeriodLookup = Map(1 -> "before midday", 2 -> "after midday")


  // main method to covert time in 24hr format to time as words
  def TimeAsWords(InputTime: String, Exp24hr: String,
                  WordsLookup: Map[Int, String], PeriodLookup: Map[Int, String]) = {

    def ExtractTime(InputTime: String, PeriodLookup: Map[Int, String]) : (Int, Int, String) = {
      // extract hours from string
      val HrsInt = InputTime.take(2).toInt
      // convert HrsInt into 12hrs using (12 - ((HrsInt) % 12)
      val Hrs12Int = 12
      // extract minutes from string
      val MinInt = InputTime.takeRight(2).toInt
      // determine PdString (PeriodLookup(2) if HrsInt >= 12 else PeriodLookup(1))
      val PdString = PeriodLookup(1).mkString

      def ConvertTime(Hrs12Int: Int, MinInt: Int, PrString: String) = {
        // convert input hours, minutes and period into time as words
        // based on if else if conditions based on Python module
        // return time as words as a string
      }
      // test if input time matches 24hr format else stop program
      // and return message that format is incorrect
      val pattern24hr = InputTime.matches(Exp24hr)
      if (InputTime.matches(Exp24hr)){
        val (Hrs12Int, MinInt, PdString) = ExtractTime(InputTime, PeriodLookup)
        val TimeWords = ConvertTime(Hrs12Int, MinInt, PdString)
        println(TimeWords)
      } else{
        println("Input time format doesn't match 24hr format: 00:00 - 23:59")
        System.exit(0)
      }
    }
  }
  TimeAsWords(InputTime, Exp24hr, WordsLookup, PeriodLookup)
}

Scala Object: TimeAsWords

enter image description here

Scala Method ExtractTime: Error Message

"""
Created on 28 May 2017

Convert 24hr format time

into time as words

@author: PeterW
"""
import sys
import re
import argparse


def words_lookup():
    """Create a dictionary
    to lookup equivalent word
    of integer number"""
    words_dict = {1: 'one', 2: 'two', 3: 'three', 4: 'four', 5: 'five',
                  6: 'six', 7: 'seven', 8: 'eight', 9: 'nine', 10: 'ten',
                  11: 'eleven', 12: 'twelve', 13: 'thirteen',
                  14: 'fourteen', 16: 'sixteen', 17: 'seventeen',
                  18: 'eighteen', 19: 'nineteen', 20: 'twenty',
                  21: 'twenty one', 22: 'twenty two', 23: 'twenty three',
                  24: 'twenty four', 25: 'twenty five', 26: 'twenty six',
                  27: 'twenty seven', 28: 'twenty eight', 29: 'twenty nine'}
    return words_dict


def validate_time_format(input_time):
    """Validate that input string
    matches require 24hr time format"""
    regexp_object = re.compile(r'^(([01]\d|2[0-3]):([0-5]\d)|23:59)$')
    if regexp_object.match(input_time):
        print("Processing: {0} into words".format(input_time))
        time_str = input_time
    else:
        print("{0} doesn't match required input format: 00:00 to 23:59"
              .format(input_time))
        sys.exit()
    return time_str


def extract_time(time_str):
    """Convert time from 24hr format
    into 12hours, minutes and period"""
    period_type = {'am': 'before midday', 'pm': 'after midday'}
    try:
        hours, minutes = int(time_str[:2]), int(time_str[3:])
        suffix = "{0}".format("pm" if hours >= 12 else 'am')
        hours = 12 - ((-hours) % 12)
        period = period_type.get(suffix)
    except ValueError as err:
        print(err)
        sys.exit()
    return hours, minutes, period


def time_conversion(words_dict, hours, minutes, period):
    """Return time as words
    based on relevant condition"""
    if hours == 12:
        hours2 = words_dict.get(1)
    else:
        hours2 = words_dict.get(hours+1)
    if hours == 12 and minutes == 0 and period == 'before midday':
        time_words = 'Midnight'
    elif hours == 12 and minutes == 0 and period == 'after midday':
        time_words = 'Midday'
    elif minutes == 0:
        time_words = "{0} o'clock {1}.".format(str(words_dict.get(hours)).title(),
                                               period)
    elif minutes == 15:
        time_words = "Quarter past {0} {1}.".format(words_dict.get(hours),
                                                    period)
    elif minutes == 30:
        time_words = "Half past {0} {1}.".format(words_dict.get(hours),
                                                 period)
    elif minutes == 45:
        time_words = "Quarter to {0} {1}.".format(hours2,
                                                  period)
    elif minutes < 30:
        min_str = words_dict.get(minutes).capitalize()
        min_num = "" if minutes == 1 else "s"
        time_words = "{0} minute{1} past {2} {3}.".format(min_str,
                                                          min_num,
                                                          words_dict.get(hours),
                                                          period)
    else:
        min_str = words_dict.get(60 - minutes).capitalize()
        min_num = "" if 60 - minutes == 1 else "s"
        time_words = '{0} minute{1} to {2} {3}.'.format(min_str,
                                                        min_num,
                                                        hours2,
                                                        period)
    return time_words


def time_as_words(input_time):
    """Convert 24hr format time
    into time as words"""
    words_dict = words_lookup()
    time_str = validate_time_format(input_time)
    hours, minutes, period = extract_time(time_str)
    time_words = time_conversion(words_dict, hours, minutes, period)
    print(time_words)


if __name__ == '__main__':
    parser = argparse.ArgumentParser(description="Convert 24hr format time into time as words")
    parser.add_argument("-t", "--input_time", metavar="time", type=str, required=True,
                        help='input time in the following format 00:00 to 23:59')
    args = parser.parse_args()
    time_as_words(input_time=args.input_time)

Python Module: TimeAsWords

object session {

  val PeriodLookup = Map(1 -> "before midday", 2 -> "after midday")

  def ExtractTime(InputTime: String, PeriodLookup: Map[Int, String]): (Int, Int, String) = {
    // extract hours from string
    val HrsInt = InputTime.take(2).toInt
    // convert HrsInt into 12hrs using (12 - ((HrsInt) % 12)
    val Hrs12 = 12
    // extract minutes from string
    val MinInt= InputTime.takeRight(2).toInt
    // determine PdString (PeriodLookup(2) if HrsInt >= 12 else PeriodLookup(1))
    val PdString = PeriodLookup(1).mkString
    new Tuple3(HrsInt, MinInt , PdString)
  }
  val (hours1, minutes1, period1) = ExtractTime("18:45", PeriodLookup)
  println(hours1, minutes1, period1)
}

Updated Version: Scala Object ExtractTime Method

1
You have declared the method ExtractTime to return a tuple (Int, Int, String) but the method is not returning anything (Unit). Make sure that the method actually returns a tuple, or declare the return type to be Unit instead of (Int, Int, String).Jesper
Please can you let me know what I'm doing wrong so that I can corrected my question instead of marking it down, thanks PeterPeter Wilson
Also, your method TimeAsWords only contains the definition of a nested method ExtractTime and nothing else. You'll need to add code to the method that actually does something, because just declaring a method doesn't do anything at runtime.Jesper
(And I did not vote it down, someone else did).Jesper
@jesper How would I go about returning the val results as a tuple within ExtractTime, might be a simple question but I'm currently stumped.Peter Wilson

1 Answers

2
votes

You are not returning anything from your ExtractTime but you have defined (Int, Int, String) as a returnType.

So either you define your function as

def ExtractTime(InputTime: String, PeriodLookup: Map[Int, String]) : Unit = {

Or you have to return (Int, Int, String) at the end of ExtractTime function as

(Hrs12Int, MinInt, PdString)

That should get you going