Does Ruby treat variables assigned an array differently than variables assigned a string? - arrays

I thought that when you declare a variable and assign it a string or an array, you are making an independent copy of that string or array. Further, that whatever you do to that variable doesn't affect the original. My assumption does not hold true with the first set of code below:
array = ["tubular", "bell", "single", "file"]
word = array[0]
word[0] = word[0].upcase
array.join(" ") # => Tubular bell single file
word # => Tubular
array[0] # => Tubular
but it does with the second:
string = "ten aviators climbing isoceles"
word = string[0]
word = word.upcase
string # => ten aviators climbing isoceles
word # => T
string[0] # => t
Could someone explain why in the first set of code, setting word[0] to word[0].upcase results in a change to the array bell_arr I originally assigned to the variable word, but a similar change does not take place with a variable assigned a string as in the second set of code?

This is has to deal with what we call mutability/immutability and object references in programming languages. A few searches with "Ruby" and "immutability", "references" would bring a few good results but basically
array = ["tubular bell single", "bell", "single", "file"]
An array holds references to some Ruby Objects, here String instances. A "Ruby string" is actually an instance of the class String. Your array contains references to some instances of String.
sentence = array[0] # Ruby terminal shows "tubular bell single"
# But actually, array[0] is a reference to a Ruby Object
Array[0] is a reference to a string Object, ie. array[0] is like a pointer to a string. But when you call array[0] in your Ruby terminal, you are actually just interested in the string representation, not the object itself, so Ruby, behind the scene, calls something like array[0].to_string so you can see the string and not just something like #<Object:0x000000031082b0>, which is what Ruby usually shows when you write in your terminal a reference to an object and press enter.
Now unlike other programming languages (like C), there is nothing "below" a String class, there is no "Character" class. A String instance isn't made of references to other Ruby objects.
When you are calling "sentence[0]", that basically means you want the first letter of the sentence as a Ruby Object that you can manipulate. So it does create a new Object in the memory.
So actually first_letter = sentence[0] creates a new String instance, which is just the string containg "t"
However, when you call sentence[0] = "t", you are calling a specific method on the String instance, which basically says : replace this letter by this one. When doing this, you are modifying the instance itself !
EDIT : maybe the following will make it clear. Ruby uses "object identifiers" to identify the instances of each Object
a = "Hello World"
b = "Hello again"
c = "Hello World" # Same string representation as a
a.object_id # 26817860
b.object_id # 25401040
c.object_id # 25247200 # Note it's not the same number as a!
array = [a, b, c]
array[0].object_id # 26817860, so it's the instance of a !!
first_letter = a[0] # Ruby shows "H"
first_letter.object_id # 23178100, a different object in memory
Now
a = "A new string"
a.object_id # 24367200, it's again new object in memory
array # ["Hello World", "Hello again", "Hello World"]
array[0].object_id # 26817860, our array[0] still points to the original object_id
But
first_item = array[0] # "Hello World"
first_item.object_id # 26817860 alright, no magic trick, this is still the object_id of "a"
first_item[0] = "X"
first_item.object_id # 26817860 UNCHANGED ! We have really changed our instance and not created a new one !
array # ["Xello World", "Hello again", "Hello World"]

It is because word = string[0] creates a new string instance. If a single character taken out of a string is the same string instance as the original string, then that would mean that the content of the original string has been replaced by that single character. That would be too inconvenient. Ruby's String#[] does not do that. It creates a new string instance. And furthermore, word = word.upcase is just a variable assignment. It uses the old word and creates a new variable word. It does not do anything to the old word (which is not the same as string in the first place).

Related

"How would you remove an element from a Ruby array that is just another element reversed?"

I need to remove a reversed word from a Ruby array and I'm having issues with getting the correct output
I've tried a different way of looping, I've used exec.
# Were going to get the first line of input which is the length of the wordlist and coerce to type fixnum
pword_list_length = gets.chomp.to_i
# Initialize an empty array to hold pword_list_length
pword_list = Array.new
# loop until we have all the lines of input pushed into the array
pword_list_length.times do
pword_list.push(gets.chomp)
end
# Next were going to check to see what word we have both forward and backwards
pword_list.each do |word|
bword = word.reverse!
if pword_list.include? bword
print word.length
print bword[bword.length/2]
end
end```
```Expected Output:
3 y
Output
3y3c3e3y```
You are mutating list here:
bword = word.reverse!
reverse! will actually reverse content of variable word. After that list will sure include bword as you just made word reversed and assigned it to bword.
Use reverse (without !) to keep word unchanged.

Passing Swift strings as C strings in variable argument lists

I am trying to use the old printf-style string formatters to define a new Swift string from an old Swift string.
I notice this works fine as long as I am starting with a Swift string literal, but not a string variable.
// OK!
String(format:"%s", "hello world".cStringUsingEncoding(NSUTF8StringEncoding))
// ERROR: argument type '[CChar]?' does not conform to expected type 'CVarArgType'
let s = "hello world"
String(format:"%s", s.cStringUsingEncoding(NSUTF8StringEncoding))
Why does this happen?
And what's the best workaround?
(Please note that I am aware of but do not want to use the Cocoa string formatter %#. I want the printf-style formatting code because what I'm actually trying to do is get quick and dirty tabular alignment with codes like %-10s.)
This question concerns Swift 2.2.
Don't create a C string just for alignment. There is a method stringByPaddingToLength for that.
// Swift 2
s.stringByPaddingToLength(10, withString: " ", startingAtIndex: 0)
// Swift 3
s.padding(toLength: 10, withPad: " ", startingAt: 0)
Note that it will truncate the string if it is longer than 10 UTF-16 code units.
The problem here is, there are two methods in Swift named cStringUsingEncoding:
func cStringUsingEncoding(encoding: UInt) -> UnsafePointer<Int8>, as a method of NSString
func cStringUsingEncoding(encoding: NSStringEncoding) -> [CChar]?, as an extension of String
If we want a pointer, we need to ensure we are using an NSString, not a String.
In the first example, a string literal can be a String or NSString, so the compiler chooses NSString since the other one won't work.
But in the second example, the type of s is already set to String, so the method that returns [CChar]? is chosen.
This could be worked-around by forcing s to be an NSString:
let s: NSString = "hello world"
String(format:"%s", s.cStringUsingEncoding(NSUTF8StringEncoding))
kennytm's answer is clearly the best way.
But for anyone else who is still wondering about how to do it the wrong way, and get access to a c string based on a Swift String without going through NSString, this also seems to work (Swift 2.2):
var s:String = "dynamic string"
s.nulTerminatedUTF8.withUnsafeBufferPointer { (buffptr) -> String in
let retval:String = String(format:"%s",buffptr.baseAddress)
return retval
}

How to read an array from a file and store it in an array

I am trying to read an array from a file and save it in myArray[].
File1.txt is ["abc", "def"...] and I want to be able to parse myArray[0] which is "abc", myArray[1] which is "def" and so on.
f = File.open('file.text')
payload = f.read
puts payload
gives output as ["abc", "def"...] which is a string and it becomes difficult to read as an array.
You will need to use Kernel#eval to parse the file contents (string) as Ruby code.
Contents of eval.txt:
['abc', 'def']
Ruby code:
contents = File.read("eval.txt")
foo = Kernel.eval(contents)
puts "Length of foo is #{foo.length}"
Outputs:
Length of foo is 2
Using JSON as your serialization format might be saner option though.

How to convert array of strings to string in D?

I have got array of strings like:
string [] foo = ["zxc", "asd", "qwe"];
I need to create string from them. Like:
"zxc", "asd", "qwe" (yes every elements need be quoted and separate with comma from another, but it should be string, but not array of strings.
How can I do it's in functional style?
import std.algorithm, std.array, std.string;
string[] foo = ["zxc", "asd", "qwe"];
string str = foo.map!(a => format(`"%s"`, a)).join(", ");
assert(str == `"zxc", "asd", "qwe"`);
std.algorithm.map takes a lambda which converts an element in the range into something else, and it returns a lazy range where each element is the result of passing an element from the original range to the lambda function. In this case, the lambda takes the input string and creates a new string which has quotes around it, so the result of passing foo to it is a range of strings which have quotes around them.
std.array.join then takes a range and eagerly concatenates each of its elements with the given separator separating each one and returns a new array, and since it's given a range of strings it returns a string. So, by giving it the result of the call to map and ", " for the separator, you get your string made up of the original strings quoted and separated by commas.
std.algorithm.joiner would do the same thing as join, except that it results in a lazy range instead of an array.
Alternatively, use std.string.format
Your format specifier should be something like this:
auto joinedstring = format("%(\“%s\",%)", stringarray)
this is untested as I'm on mobile, but it's something similar to it!

cdr string in ML language

I try to find a library function in ML that equal to (cdr string) in Scheme (meaning (cdr abcd) = bcd).
(Asuming SML)
Another way is to convert the string to a list of chars (explode), then you have the option to take the head (hd) or tail (tl), and then finally convert it back to a string (implode):
- (implode o tl o explode) "this is a string";
val it = "his is a string" : string
The string conversion functions can be found in the String module, and the head and tail functions can be found in the List module
Obviously you can also use the substring method here, however in SML you have the extract function that are quite convenient in this case:
- String.extract("This is a string", 1, NONE);
val it = "his is a string" : string
Giving it the NONE argument makes it extract until the end of the string.
Assuming the Ocaml dialect, you could use the standard String module with e.g.
let rest_str str =
let slen = String.length str in
String.sub str 1 (slen-1)
;;

Resources