iT邦幫忙

0

面試題討論

sion 2019-12-03 17:18:472652 瀏覽
  • 分享至 

  • xImage

今天去面試時,面試官認為我的程式碼不夠簡潔,使用太多的if了
題目是要將一串數字金額轉成英文
例:

input : 10000
ouput : Ten thousand dollars
-----------------------
input : 1999999
ouput : One million,nine hundred and ninety nine thousand,nine hundred and ninety nine dollars
-----------------------
input : 123456789
ouput : One hundred and twenty three million,four hundred and fifty six thousand,seven hundred and eighty nine dollars

我寫法是每3位數去切1|23這樣,然後再if換英文
畢竟還有11~19這種不規則的數字英文,所以我很蠢的寫了大概30個if處理1~10,11~19,20~99
雖然還有其他細部的規則要用if處理,不過我想應該是這段讓面試官看得不順眼吧
版上有大大有更好更簡潔的方式處理嗎?

圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中
2
海綿寶寶
iT邦大神 1 級 ‧ 2019-12-03 17:43:54

如果有時間
可以參考國際網友的寫法

選我最佳解答

以下是另一篇 C# 的寫法
你認為被打槍的一堆 if 的部份
作者是用「查表」的方式處理
可以參考看看

public static string NumberToWords(int number)
{
    if (number == 0)
        return "zero";

    if (number < 0)
        return "minus " + NumberToWords(Math.Abs(number));

    string words = "";

    if ((number / 1000000) > 0)
    {
        words += NumberToWords(number / 1000000) + " million ";
        number %= 1000000;
    }

    if ((number / 1000) > 0)
    {
        words += NumberToWords(number / 1000) + " thousand ";
        number %= 1000;
    }

    if ((number / 100) > 0)
    {
        words += NumberToWords(number / 100) + " hundred ";
        number %= 100;
    }

    if (number > 0)
    {
        if (words != "")
            words += "and ";

        var unitsMap = new[] { "zero", "one", "two", "three", "four", "five", "six", "seven", "eight", "nine", "ten", "eleven", "twelve", "thirteen", "fourteen", "fifteen", "sixteen", "seventeen", "eighteen", "nineteen" };
        var tensMap = new[] { "zero", "ten", "twenty", "thirty", "forty", "fifty", "sixty", "seventy", "eighty", "ninety" };

        if (number < 20)
            words += unitsMap[number];
        else
        {
            words += tensMap[number / 10];
            if ((number % 10) > 0)
                words += "-" + unitsMap[number % 10];
        }
    }

    return words;
}
圓頭人 iT邦研究生 5 級 ‧ 2019-12-04 09:17:55 檢舉

不能看,要付錢才能看?

0
小魚
iT邦大師 1 級 ‧ 2019-12-03 19:48:55
  1. 哪種程式語言?
  2. 說好的程式碼呢?
看更多先前的回應...收起先前的回應...

聰明的人才看到的/images/emoticon/emoticon07.gif

sion iT邦新手 4 級 ‧ 2019-12-04 09:24:30 檢舉

演算法當然不限語言阿
我是用C#
程式碼大概這樣

if(a==1)
    return one;
else if(a==2)
    return two;
    .
    .
    .
if(a==11)
    return eleven;
    .
    .
    .
小魚 iT邦大師 1 級 ‧ 2019-12-04 09:48:19 檢舉

那不如,
來個switch吧.

其實正則好像就可以處理了。

用 array 應該邏輯比較簡單

2

你知道,什麼叫做backcall應用嘛。

看更多先前的回應...收起先前的回應...
sion iT邦新手 4 級 ‧ 2019-12-04 09:21:59 檢舉

您是說callback嗎?
backcall真的沒聽過

那就沒聽過吧。

WQ iT邦新手 2 級 ‧ 2019-12-04 09:41:36 檢舉

中文叫"遞迴"

人家要說我說錯了。你就讓他認為我說錯了就好了。
不要破梗啦!!

WQ iT邦新手 2 級 ‧ 2019-12-04 09:44:24 檢舉

拍謝,我只是新手...我錯了~

算了,我以前的做法給你參考。
我會先將各位數的「個十百千萬...」的位數英文準備好。
再將0到9的英文也準備好

先直接照著其位數跟數值轉譯後。
再用正則取代的方式。將如「一拾零」的用語轉成「拾」

雖然當初我是應用在國字上。理當也可以應用在英文上才對。
給你參考吧。11~19特殊的轉譯。因為我對英文數字的解釋不太懂。
所以沒辦法給你有效的參考。只能給你大約的觀念。

froce iT邦大師 1 級 ‧ 2019-12-04 11:29:36 檢舉

ㄜ...我只知道遞迴是recursive,backcall這我還真的第一次看見。哈

的確是 recursive
不過我這邊習慣講 backcall 跟 callback 不一樣。
要解釋的話。

backcall 就是再call自已一次。

callback 就是準備好一個程序等待回應回呼。

認真來說,backcall這個用詞是我很早期就在用的說法。以前都是用在function上的。常用的範例就是計時器處理。認真來說當時這說法是用在javascript上的。

那時還比較沒有物件導向的東西在。
當開始有了物件及mvc理念出來時。recursive隨即而生,就英文意思而言也算是比較接近的用語就是了。

可是..................我習慣叫backcall你咬我喔!!
(抱歉,今天本人28天來,有點無理取鬧中)

0
listennn08
iT邦高手 5 級 ‧ 2019-12-04 11:28:11

Sample
英文基本上就是 3 個 0 一個單位

Billion 1,000,000,000
Million 1,000,000
Thousand 1,000
剩下的就是 Hundred 的部分
特殊處理只有小於20
units = ['', 'Thousand', 'Million', 'Billion']
lessThanTwenty = ['', 'One', ..., 'Nineteen']
tenDigits = ['', 'Ten', ..., 'Ninety']

既然知道 3 個 0 一個單位
那就每次都取 1000 的餘數就好了
再加上單位的部分

while (num > 0) {
    num3 = _Hundred(num%1000)
    if (num3) {
        res.unshift(`${num3} ${units[i]}`);
    }
    num = Math.floor(num/1000);
    i++
}
return res.join(' and ');

把取出來的數放到另一個 function 去處理

const _Hundred =  (num) => {
    if (num < 20) {
        return `${lessThanTwenty[num]}`
    } else if (num < 100) {
        return `${tenDigits[Math.floor(num/10)]} ${_Hundred(num%10)}`
    } else {
        return `${lessThanTwenty[Math.floor(num /100)]} Hundred ${_Hundred(num % 100)}`
    }
}

邏輯大概就這樣

1
ccutmis
iT邦高手 2 級 ‧ 2019-12-04 12:44:48

提供一個糙code給你參考
test.htm

<script>
const alias_arr=['Trillion','Billion','Million','Thousand'];
const dig_str_dict={'0':'none','1':'One',  '2':'Two','3':'Three','4':'Four','5':'Five','6':'Six','7':'Seven','8':'Eight','9':'Nine'}; //百位數&個位數
const teen_str_dict={
  '10':'Ten','11':'Eleven','12':'Tweleve','13':'Thirteen','14':'Fourteen','15':'Fifteen','16':'Sixteen','17':'Seventeen','18':'Eighteen','19':'Nineteen'
}; //10~19的特別處理
const ten_str_dict={
  '0':'none','1':'none','2':'Twenty','3':'Thirty','4':'Fourty','5':'Fifty','6':'Sixty','7':'Seventy','8':'Eighty','9':'Ninety'
}; //非10~19的十位數處理

//用正則把'123456'轉成'123,456'的函式
function numToCommaStr(input_num) {
  return num.toString().replace(/(\d)(?=(?:\d{3})+$)/g, "$1,");
}

function numStrToFinanStr(input_str){
  console.log('Input: '+input_str) // 傳入字串
  const tmp_arr=input_str.split(',');
  if(tmp_arr.length<2){
    return strToDesStr(tmp_arr[0],'');
  }else{
    const aliasFlag=(tmp_arr.length==2?3:(tmp_arr.length==3?2:(tmp_arr.length==4?1:0)));
    let outstr='';
    for(let ti=0;ti<tmp_arr.length;ti++){
      const tmpDesStr=strToDesStr(tmp_arr[ti],(aliasFlag+ti<4?alias_arr[aliasFlag+ti]:''));
      outstr+=(ti>0&&(tmpDesStr!=' ')?', ':'')+tmpDesStr;
    }
    return 'Output: ' + outstr +'dollars.';
  }
}

function strToDesStr(input_str,baseStr){
  const tmp_num=parseInt(input_str[0]=='0'?input_str[1]+input_str[2]:input_str);
  const hunNum=Math.floor(tmp_num/100);
  const tenDigNum=Math.floor(tmp_num%100);
  const tenNum=Math.floor(tenDigNum/10);
  const digNum=Math.floor(tenDigNum%10);
  //console.log('tmp_num:'+tmp_num+ ' hunNum:'+hunNum+' tenDigNum:'+tenDigNum+ ' tenNum:'+tenNum+ ' digNum:'+digNum);
  let tmpStr = (hunNum!==0)? dig_str_dict[hunNum.toString(10)] + ' hundred':'';
  if(tenDigNum>19||tenDigNum<10){
    //非10~19在這邊處理
    tmpStr+= (tmpStr!=''?' and ':'')+(tenNum!=0?ten_str_dict[tenNum.toString(10)]+' ':'') + (digNum!=0?dig_str_dict[digNum.toString(10)]:'');
  }else{
    //10~19在這邊處理
    tmpStr+= (tmpStr!=''?' and ':'')+ teen_str_dict[tenDigNum.toString(10)];
  }
  return tmpStr +' '+ baseStr;
}

let num='10000';
console.log(numStrToFinanStr(numToCommaStr(num)));

num='1999999';
console.log(numStrToFinanStr(numToCommaStr(num)));

num='123456789';
console.log(numStrToFinanStr(numToCommaStr(num)));
</script>

糙code大致思路如下:
1.先宣告相關字典變數 dig_str_dict 處理個位數與百位數 , teen_str_dict 處理10~19數字 ,ten_str_dict處理十位數
2.輸入字串加上千分符的部份用正則處理
3.然後把 輸入字串加上千分符 用','分割傳回陣列 tmp_arr
4.最後用 tmp_arr 跑一次廻圈套字典處理完就完成了。
很多地方你會看到有(?:)的結構,每一個(cond?ttt:fff)你可以看作是一個if(cond){ttt}else{fff},這個三元運算子的用法可以多利用,好處就是程式行數可以縮減,壞處就是易讀性會變差。

===

被人嫌if太多,可以試試用3元運算子(?:)嚇他,例如:

<script>
let cardNo=1;
cardFace=cardNo>2&&cardNo<11?cardNo:(cardNo==11?'J':(cardNo==12?'Q':(cardNo==13?'K':'A')));
/* 上面這行等同於 if(cardNo>2&&cardNo<11){...}else if(cardNo==11){...}else if(cardNo==12){...}else if(cardNo==13){...}else{...} */
console.log(cardFace);
</script>
0
ckp6250
iT邦好手 1 級 ‧ 2019-12-04 17:33:47

好像比轉中文大寫金額還嚕嗦?

0
marlin12
iT邦研究生 5 級 ‧ 2019-12-05 00:28:58

C# 程式在綫測試

using System;

class Program
{
  static void Main()
  {
    Console.WriteLine( NumberToWord(10000) );

    Console.WriteLine( NumberToWord(1999999) );

    Console.WriteLine( NumberToWord(123456789) );
  }

  static string NumberToWord(int value)
  {
    string[] scaleWords = { "", "throusand", "million", "billion" };

    string result = "";

    for(int x = 0; value != 0; x++, value /= 1000)
    {
      string block = ThreeDigitsToWord(value % 1000);

      if(block.Length > 0) {
        result = block + scaleWords[x] + ((result.Length > 0) ? "," + result : ((x > 0) ? " " : ""));
      }
    }

    return FirstCharToUpper(result + "dollars");
  }

  static string ThreeDigitsToWord(int value)
  {
    string[] under20Words = { "", "one ", "two ", "three ", "four ", "five ", "six ", "seven ", "eight ", "nine ", "ten ", "eleven ", "twelve ", "thirteen ", "fourteen ", "fifteen ", "sixteen ", "seventeen ", "eighteen ", "nineteen " };

    string[] tenthWords = { "", "", "twenty ", "thirty ", "forty ", "fifty ", "sixty ", "seventy ", "eighty ", "ninety " };

    int valDiv100 = value / 100;

    string front = (valDiv100 > 0) ? under20Words[valDiv100] + "hundred " : "";

    int valMod100 = value % 100;

    string back = (valMod100 < 20) ? under20Words[valMod100] : tenthWords[valMod100 / 10] + under20Words[valMod100 % 10];

    return front + (((front.Length * back.Length) > 0) ? "and " : "") + back;
  }

  static string FirstCharToUpper(string input) =>
      char.ToUpper(input[0]) + input.Substring(1);
}
0
YC
iT邦好手 1 級 ‧ 2019-12-09 16:35:47

抱歉,我是來亂的XD
use swift

import Foundation

let format = NumberFormatter()
format.numberStyle = .spellOut
format.locale = Locale(identifier: "en")
for value in [10000, 1999999, 123456789] {
    print("input : \(value)")
    
    let number = NSNumber(value: value)
    print("ouput : \(format.string(from: number) ?? "--") dollars")
}

我要發表回答

立即登入回答