statistics.go 2.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104
  1. // statistics.go
  2. package main
  3. import (
  4. "fmt"
  5. "net/http"
  6. "sort"
  7. "strings"
  8. "strconv"
  9. "log"
  10. )
  11. type statistics struct {
  12. numbers []float64
  13. mean float64
  14. median float64
  15. }
  16. const form = `<html><body><form action="/" method="POST">
  17. <label for="numbers">Numbers (comma or space-separated):</label><br>
  18. <input type="text" name="numbers" size="30"><br />
  19. <input type="submit" value="Calculate">
  20. </form></html></body>`
  21. const error = `<p class="error">%s</p>`
  22. var pageTop = ""
  23. var pageBottom = ""
  24. func main() {
  25. http.HandleFunc("/", homePage)
  26. if err := http.ListenAndServe(":9001", nil); err != nil {
  27. log.Fatal("failed to start server", err)
  28. }
  29. }
  30. func homePage(writer http.ResponseWriter, request *http.Request) {
  31. writer.Header().Set("Content-Type", "text/html")
  32. err := request.ParseForm() // Must be called before writing response
  33. fmt.Fprint(writer, pageTop, form)
  34. if err != nil {
  35. fmt.Fprintf(writer, error, err)
  36. } else {
  37. if numbers, message, ok := processRequest(request); ok {
  38. stats := getStats(numbers)
  39. fmt.Fprint(writer, formatStats(stats))
  40. } else if message != "" {
  41. fmt.Fprintf(writer, error, message)
  42. }
  43. }
  44. fmt.Fprint(writer, pageBottom)
  45. }
  46. func processRequest(request *http.Request) ([]float64, string, bool) {
  47. var numbers []float64
  48. if slice, found := request.Form["numbers"]; found && len(slice) > 0 {
  49. text := strings.Replace(slice[0], ",", " ", -1)
  50. for _, field := range strings.Fields(text) {
  51. if x, err := strconv.ParseFloat(field, 64); err != nil {
  52. return numbers, "'" + field + "' is invalid", false
  53. } else {
  54. numbers = append(numbers, x)
  55. }
  56. }
  57. }
  58. if len(numbers) == 0 {
  59. return numbers, "", false // no data first time form is shown
  60. }
  61. return numbers, "", true
  62. }
  63. func getStats(numbers []float64) (stats statistics) {
  64. stats.numbers = numbers
  65. sort.Float64s(stats.numbers)
  66. stats.mean = sum(numbers) / float64(len(numbers))
  67. stats.median = median(numbers)
  68. return
  69. }
  70. func sum(numbers []float64) (total float64) {
  71. for _, x := range numbers {
  72. total += x
  73. }
  74. return
  75. }
  76. func median(numbers []float64) float64 {
  77. middle := len(numbers)/2
  78. result := numbers[middle]
  79. if len(numbers)%2 == 0 {
  80. result = (result + numbers[middle-1]) / 2
  81. }
  82. return result
  83. }
  84. func formatStats(stats statistics) string {
  85. return fmt.Sprintf(`<table border="1">
  86. <tr><th colspan="2">Results</th></tr>
  87. <tr><td>Numbers</td><td>%v</td></tr>
  88. <tr><td>Count</td><td>%d</td></tr>
  89. <tr><td>Mean</td><td>%f</td></tr>
  90. <tr><td>Median</td><td>%f</td></tr>
  91. </table>`, stats.numbers, len(stats.numbers), stats.mean, stats.median)
  92. }