루비 초보입니다. 간단한 문제를 풀려고 하는데 쉽게 되질 않네요.

isty2e의 이미지

http://programming-challenges.com/pg.php?page=downloadproblem&probid=110102&format=html

이 문제를 풀려고 다음과 같이 코드를 짰습니다만

gets=~/(\d+)\s(\d+)/
m, n=$1.to_i, $2.to_i
field, num=[m][n]
0.upto(m) {|i| gets=~/([*.])*\s/; 0.upto(n) {|j| field[i][j]=$j}}
def adj(minefield)
id=0
for k in (i==0)? (i):(i-1)..(i==m)? (i):(i+1)
for l in (j==0)? (j):(j-1)..(j==n)? (j):(j+1)
id+=1 if (minefield[k][l]=='*')
end
end
return id
end
0.upto(m) {|i| 0.upto(j) {|j| print num[i][j]=adj(field[i][j])}; puts ""}

Problem 002 Inbae$ ruby Problem002.rb
3 5
*.***
Problem002.rb:4: undefined method `[]' for nil:NilClass (NoMethodError)
from Problem002.rb:4:in `upto'
from Problem002.rb:4
from Problem002.rb:4:in `upto'
from Problem002.rb:4

라네요. 뭐가 문제인지 잘 모르겠습니다... 어떻게 하면 개선할 수 있는지, 그리고 어떻게 하면 코드를 좀 단축할 수 있을지 알고 싶습니다. 일차원 배열을 사용하면 더 쉽겠지만 일부러 이차원 배열을 써 봤습니다.

아, 그리고 곡괭이 책을 보면서 공부하고 있는데, 책 구성이 여태 보던 언어 책이랑 좀 달라서 지식이 체계화가 잘 안 되네요. 참고할 만한 보조 자료 있을까요? 한국어면 더 좋구요.

klenui의 이미지

ruby는 잘모르지만.. field가 nil이라는 이야기네요..

field, num=[m][n]

이부분이 잘못된거 같은데... 원하시는게 m x n 2차 배열 만드는 거라면.. 관련 문법을 참조해 보시면 될듯합니다.
구글링 해보니

field = Array.new(m)
for i in (0..m)
    field[i] = Array.new(n)
end

뭐 이렇게 하라는 거 같은데.. 더 좋은 코드는 다른 분들이 달아주시겠지요..

neocoin의 이미지

m,n=4,5
field = (1..m).map{|i|[nil]*n}

bootmeta의 이미지

m,n=4,5
field = [[nil] * n] * m

neocoin의 이미지

Ruby의 Array의 * 연산자가 deep copy를 하지 않습니다. 그래서 fields array 내부의 n 길이의 array가 모두 같은 참조값을 가집니다.

이렇게 확인할수 있습니다.

irb(main):009:0> a=["A"]*10
=> ["A", "A", "A", "A", "A", "A", "A", "A", "A", "A"]
irb(main):010:0> a[0].sub!("A", "B")
=> "B"
irb(main):011:0> a
=> ["B", "B", "B", "B", "B", "B", "B", "B", "B", "B"]
irb(main):012:0> 

bootmeta의 이미지

역시 안써봤던 repeat 연산자를 아무 생각없이 사용했더니 흐흐

lateau의 이미지

field, num=[m][n] # => nil

만약 2차원 배열을 동적으로 만들고 싶다면

[] << []

이 방법으로 생성가능하옵니다.

- Why don't you come in OpenSolaris? I hope you come together.

--
I think to myself...what a emerging world.

isty2e의 이미지

다들 감사합니다.

gets=~/(\d+)\s(\d+)/
m, n=$1.to_i, $2.to_i
field, num=(1..m).map{|i| [nil]*n}
0.upto(m) {|i| gets=~/([*.])*\s/; 0.upto(n) {|j| field[i][j]=$j}}
def adj(minefield)
id=0
for k in (i==0)? (i):(i-1)..(i==m)? (i):(i+1)
for l in (j==0)? (j):(j-1)..(j==n)? (j):(j+1)
id+=1 if (minefield[k][l]=='*')
end
end
return id
end
0.upto(m) {|i| 0.upto(j) {|j| print num[i][j]=adj(field[i][j])}; puts ""}

이렇게 바꿔 봤는데 line 4에서 에러가 나는군요. 한 번에 선언과 처리를 동시에 할 수 있을 것 같기도 한데... 아직 잘 모르겠네요. 코드가 루비스럽지도 않은 것 같고 말이죠. 뭔가 우아하고 깔끔하면서도 짧게 끝나는 방법 없을까요?

lateau의 이미지

field, num=(1..m).map{|i| [nil]*n} #=> nil

Enumerable#map은 &block으로 전달된 식을 평가한 값으로 배열을 되돌려 줍니다.
[nil] * n #=> [nil, nil, ... ] 이 되겠죠. 1..m 이라는 Range 클래스에의해 m번 반복되겠군요.

[
[nil, nil, ...]
[nil, nil, ...]
...
[nil, nil, ...]
]

반복되었습니다. field, num = 이라는 부분에서 앞서 나온 map의 결과물인 배열이 '잘려서' 저장됩니다.

p field #=> [nil, nil, ....]
p num   #=> [nil, nil, ....]

그리고 다음 부분에서 에러가 납니다.

0.upto(m) {|i| gets=~/([*.])*\s/; 0.upto(n) {|j| field[i][j]=$j}}

문제는 이 부분 field[i][j] 으로 field가 2차원 배열로 선언되어있지 않기 때문입니다.
여기까지면 에러에 대한 이유가 설명이 될 거라 생각합니다. 1.8.7에서 irb로 간단히 해본 것이니 문제가 있을 수도 있습니다만... -_-;;

----
그리고 잡설...

'루비'다운 건 없습니다. 우리의 무시무시한 Matz씨의 말처럼 루비는 프로그래밍을 즐길 수 있게 만들 언어이기 때문이죠.
'구조적' 으로 짤수도 있고 '객체지향적'으로 짤 수도 있습니다.
&block를 남발하여 자신과 타인을 괴롭힐수도 있고, brace를 쓰던 do end를 쓰던 자기 마음입니다. # design pattern까지 개떡같이 하자는 말은 아니구요 :)

루비다움에 너무 구속되지마세요. 자기가 짜는 게 바로 루비가 아닐까 생각합니다.
사실 짜는 사람마다 다 틀려요. 지금까지 제가 봐온 바대로라면요. :)
그리고 그걸로 토다는 사람은 아직 못 봤습니다. 그냥 자유롭게 짜세요.
루비로 짜면 반드시 타 언어에 비해 50% 코드가 줄어든다! 이런 게 정답은 아니잖아요. :)

잡설이 길었습니다. :)
요즘 Rails의 환상에 빠져 Ruby는 어때? 라고 묻는 사람이 주변에 좀 있다보니 '반' Rails적인 잡설이 되었습니다. 별로 못 느끼실지도 모르겠지만... :)

- Why don't you come in OpenSolaris? I hope you come together.

--
I think to myself...what a emerging world.

isty2e의 이미지

그럼 Array.new로 선언하고 각 원소에 대해 Array.new로 선언하는 것과 map으로 선언하는 것이 다른 건가요?

그리고

gets=~/(\d+)\s(\d+)/
m, n=$1.to_i, $2.to_i
field=Array.new(m); num=Array.new(m)
0.upto(m) {|i| field[i]=Array.new(n); num[i]=Array.new(n)}
0.upto(m) {|i| gets=~/([*.])*\s/; 0.upto(n) {|j| field[i][j]=$j}}
def adj(minefield)
id=0
for k in (i==0)? (i):(i-1)..(i==m)? (i):(i+1)
for l in (j==0)? (j):(j-1)..(j==n)? (j):(j+1)
id+=1 if (minefield[k][l]=='*')
end
end
return id
end
0.upto(m) {|i| 0.upto(n) {|j| print num[i][j]=adj(field[i][j])}; puts ""}

로 바꾸었는데, 에러가 나는군요... adj 함수의 문제인가요? 원래 adj(minefield[i][j])로 하려고 했는데 역시나 안 되더군요. 루비를 쓰면 뭐가 가능하고 뭐가 불가능한지 모르겠네요 -_-;;

여하튼 0.upto(m) {|i| 0.upto(n) {|j| print num[i][j]=adj(field[i][j])}; puts ""}를 유지시키는 범위에서 adj를 짜려면 어떻게 해야 하나요?

lateau의 이미지

Quote:

그럼 Array.new로 선언하고 각 원소에 대해 Array.new로 선언하는 것과 map으로 선언하는 것이 다른 건가요?

결과적으로 다를 수도 있고 같을 수도 있습니다.

field, num = somthing.map do ... end

일 경우 map은 배열을 반환하고 field, num = 의 = 연산자 법칙에 따라 그 배열 자체가 field, num에 저장되지 않고 [0]와 [1]이 각각 저장됩니다.
field가 2차원 배열이 될 수 없었던 이유가 여기 있습니다. map에 의해 반환되는 배열이 2차원 배열이었기 때문에 결과적으로 2차원 배열의 index 0과 1의 값이 저장되었기 때문이죠.
map으로 반환되는 값이 3차원일 경우에는 2차원 배열이 각각 저장되겠네요. :)

Array.new로 선언할 경우도 마찬가지입니다. 위 코드에서 문제가 되었던 부분은 field, num = 방식의 = 연산자 부분과 map이 반환하는 값이 2차원 배열이었다는 점이네요. :)

그리고 [nil]*4의 경우 [nil] 이라는 배열 4개가 생성될 것 같지만 사실은 '4개의 요소를 가진 1차원 배열'이 생성됩니다. 이 부분도 주의를 하시면 되겠네요.

Quote:

루비를 쓰면 뭐가 가능하고 뭐가 불가능한지 모르겠네요 -_-;;

안되는 것 많습니다. :)
아래와 같은 소스는 parsing 자체가 불가능하죠.

irb> "test".gsub /e/, "E".sub /E/, "e"
SyntaxError: compile error
(irb):5: syntax error, unexpected tREGEXP_BEG, expecting $end
"test".gsub /e/, "E".sub /E/, "e"
                  ^
	from (irb):5
	from :0

()로 method call 부분을 명확하게 해주면 문제가 없어집니다.

irb> "test".gsub /e/, "E".sub(/E/, "e")
=> "test"

이 부분외에도 많을 거라 생각합니다. 루비를 짜실 때 일단 생각해보면 좋은 것은

1. perl, c 언어에서 비슷한 기능이 있었는가 # => 루비가 보수적이기 때문입니다. 본래 다른 언어에 있었던 기능이 포함이 되어있다면 그 사용법을 참고하는 것도 괜찮은 방법입니다.
2. {} / do end 어느 쪽으로 통일할 것인가 # => 가독성의 문제군요. 전 do end를 선호하는 편입니다. {}의 경우보다 do end를 쓸 경우 parser에 걸리는 부분이 좀 더 있기 때문에 어쩔 수 없이 소스가 깔끔해지는 경향이 있는 것 같습니다.
3. () 를 쓸 것인가? # => ()를 쓸 때와 쓰지 않을 때 코드의 구성(parse 문제)이 조금 달라집니다. Rails의 Model을 예로 들면

# () 안쓰기
test = Dog.find :all,
  :select => "id, name, sex",
  :order => "id DESC"
 
# () 쓰기
test = Dog.find(
  :all,
  :select => "id, name, sex",
   :order => "id DESC"
)

()를 쓸 경우 parser가 판단할 수 있는 범위가 명확해지므로, "모든 것은 文이다" 라는 루비의 기본 개념을 충실히 보장하게 됩니다만 반대로 '허용되는 모든 방법'을 사용하게 되므로 반대로 소스가 지저분해지는 경우도 많습니다.

---
또 주저리 주저리 길었습니다. 문제를 스스로 해결하시는데 중점을 두고(링크해주신 문제가 뭐하는 문제인지 영어가 딸려 몰라서 그러는 겁니다^^) 힌트(=, map)만 드립니다.
소스는 아래에 다른 분들이 써주신 걸 참고하시면 되겠네요. :)

- Why don't you come in OpenSolaris? I hope you come together.

--
I think to myself...what a emerging world.

neocoin의 이미지

adj 부는 다시 작성하시면 되구요.
파싱만 조금 루비 메소드들 사용한다면 이 정도 하면 루비 느낌을 느낄수 있겠네요.

m, n = gets.split
num, field = [],[]
 
m.to_i.times do
  field << gets.chop.split('')
  num   << [nil]*n.to_i
end
 
require 'pp'
pp field
pp num

그런데 이건 다른 언어를 해도 비슷할 겁니다. ;;

lateau의 이미지

깔끔하고 좋네요. :)

- Why don't you come in OpenSolaris? I hope you come together.

--
I think to myself...what a emerging world.

bootmeta의 이미지

#!/usr/bin/ruby1.9
# encoding: utf-8
 
def show_matrix(matrix, n, m, title= "")
  puts title
  1.upto(n) { |i|  
    1.upto(m) { |j|
      print (matrix[i][j] < 0 ? "*" : matrix[i][j]) 
    }
    puts ""
  }
end
 
ARGV << "mimes.txt"
 
loop_cnt = 0
while (true)
  nm = gets.split(' ', 2)
  n, m = nm[0].to_i, nm[1].to_i
 
  break unless (n > 0) && ( m > 0)
 
  loop_cnt += 1
 
  matrix = Array.new(n+2).map{ Array.new(m+2) }
  0.upto(n+1) { |i| 0.upto(m+1) { |j| matrix[i][j] = 0 } }
 
  (1..n).each { |i|
    line = gets[/([*.])*/]
    (1..m).each { |j|
 
      if line[j-1] == "*"
        matrix[i][j] = -100
        (i-1).upto(i+1) { |r| 
          (j-1).upto(j+1) { |c|
            matrix[r][c] += 1
          }
        }
      end
    }
  }
 
  show_matrix(matrix, n, m, "Field # #{loop_cnt}:")
end
불편한웹의 이미지

이 책을 추천합니다. 루비 1.9 까지 버커합니다.

The Ruby Programming Language
http://oreilly.com/catalog/9780596516178
By David Flanagan, Yukihiro Matsumoto
Publisher: O'Reilly Media
Released: January 2008
Pages: 448

아래 책은 얇지만 일목요연하게 설명되어 있습니다.
루비 1.8.x 까지 커버합니다.

Ruby Pocket Reference
http://oreilly.com/catalog/9780596514815
By Michael Fitzgerald
Publisher: O'Reilly Media
Released: July 2007
Pages: 180

댓글 달기

Filtered HTML

  • 텍스트에 BBCode 태그를 사용할 수 있습니다. URL은 자동으로 링크 됩니다.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>
  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.

BBCode

  • 텍스트에 BBCode 태그를 사용할 수 있습니다. URL은 자동으로 링크 됩니다.
  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param>
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.

Textile

  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • You can use Textile markup to format text.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>

Markdown

  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • Quick Tips:
    • Two or more spaces at a line's end = Line break
    • Double returns = Paragraph
    • *Single asterisks* or _single underscores_ = Emphasis
    • **Double** or __double__ = Strong
    • This is [a link](http://the.link.example.com "The optional title text")
    For complete details on the Markdown syntax, see the Markdown documentation and Markdown Extra documentation for tables, footnotes, and more.
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>

Plain text

  • HTML 태그를 사용할 수 없습니다.
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.
  • 줄과 단락은 자동으로 분리됩니다.
댓글 첨부 파일
이 댓글에 이미지나 파일을 업로드 합니다.
파일 크기는 8 MB보다 작아야 합니다.
허용할 파일 형식: txt pdf doc xls gif jpg jpeg mp3 png rar zip.
CAPTCHA
이것은 자동으로 스팸을 올리는 것을 막기 위해서 제공됩니다.