Problem overview
The function reverseWords takes a string, input, made of words separated by single spaces, and returns the same words in reverse order, joined by one space. A word is a run of non-space characters. For "sky is blue" the answer is "blue is sky". The letters inside each word keep their order. Only the order of the words changes.
Approach
The problem splits into three small jobs: cut the string into words, reverse the list of words, and glue the list back into a string. Reversing characters would be the wrong tool, because it would also flip each word. Working on the list of words reverses their order and leaves every word as it was.
Go's standard library covers the first and the last job. For the middle one, Go has no built-in slice reversal, so the lesson reverses the slice in place with two indices that move toward each other, swapping as they go.
Step by step
- Call
strings.Fields(input)and keep the resulting slice inwords. For"sky is blue"it holds["sky", "is", "blue"]. - Write a
forloop that declaresiat 0 andjatlen(words)-1, runs whilei < j, and moves both indices after each pass:iforward,jback. - Swap
words[i]andwords[j]in the loop body. - Join the slice with a single space and return the result.
Here is the swapping on three words. First i = 0 and j = 2, so sky and blue swap. Next i = 1 and j = 1, the condition i < j fails, and the loop ends. The middle word stays where it is, which is correct for an odd count. The slice is now ["blue", "is", "sky"].
The solution in Go
func reverseWords(input string) string {
words := strings.Fields(input)
for i, j := 0, len(words)-1; i < j; i, j = i+1, j-1 {
words[i], words[j] = words[j], words[i]
}
return strings.Join(words, " ")
}
Language details worth knowing
Two variables in a loop header. A Go for loop has one init statement, one condition and one post statement. To run two indices you declare both in the init with i, j := 0, len(words)-1 and update both in the post statement with a parallel assignment, i, j = i+1, j-1. You can't write i++, j-- there, because those are separate statements.
Swapping without a temporary. In words[i], words[j] = words[j], words[i], Go evaluates the whole right side before it assigns anything, so the two values trade places safely.
Slices are modified in place. words is a slice, and the swaps change its elements directly. This is safe here, because strings.Fields returns a fresh slice that nothing else uses.
Fields versus Split. strings.Fields splits around runs of white space and never returns empty strings. strings.Split(s, " ") would return an empty string for every extra space. Tried on "a b", which has two spaces, Split gives ["a" "" "b"], while Fields gives ["a" "b"]. The statement promises single spaces, so either works on the tests, but Fields is also safe with messy input.
Join takes the slice first. The signature is strings.Join(elements, separator). The function also needs the strings package imported.
Complexity
Time is O(n), where n is the length of the string: Fields scans it once, the swapping touches each word at most once, and Join copies each character once. Space is O(n) for the slice of words and the output string.
Edge cases and common mistakes
- A single word comes back unchanged:
len(words)-1is 0, so the loop doesn't run. - An empty string gives an empty slice. Then
jstarts at-1,i < jis false, andJoinreturns"". No panic occurs. - Extra leading, trailing or repeated spaces disappear, because
Fieldsignores them. For" a b "the result is"b a". - Looping until
i < len(words)instead of stopping at the middle would swap every pair twice and restore the original order. - Using
i++, j--in the loop header is a syntax error.
Testing
"sky is blue"gives"blue is sky"."one"gives"one"."a b"gives"b a".""gives"".